wined3d: Pass a struct wined3d_shader_register to shader_is_scalar().
[wine/hacks.git] / dlls / wined3d / surface.c
blob9e9d55c5b331a4f91a3a5d769f7aaf365d369328
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
35 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
41 void surface_force_reload(IWineD3DSurface *iface)
43 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
45 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
48 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
50 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
51 GLuint *name;
52 DWORD flag;
54 if(srgb)
56 name = &This->glDescription.srgbTextureName;
57 flag = SFLAG_INSRGBTEX;
59 else
61 name = &This->glDescription.textureName;
62 flag = SFLAG_INTEXTURE;
65 TRACE("(%p) : setting texture name %u\n", This, new_name);
67 if (!*name && new_name)
69 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
70 * surface has no texture name yet. See if we can get rid of this. */
71 if (This->Flags & flag)
72 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
73 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
76 *name = new_name;
77 surface_force_reload(iface);
80 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
82 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
84 TRACE("(%p) : setting target %#x\n", This, target);
86 if (This->glDescription.target != target)
88 if (target == GL_TEXTURE_RECTANGLE_ARB)
90 This->Flags &= ~SFLAG_NORMCOORD;
92 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
94 This->Flags |= SFLAG_NORMCOORD;
97 This->glDescription.target = target;
98 surface_force_reload(iface);
101 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
102 int active_sampler;
104 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
105 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
106 * gl states. The current texture unit should always be a valid one.
108 * To be more specific, this is tricky because we can implicitly be called
109 * from sampler() in state.c. This means we can't touch anything other than
110 * whatever happens to be the currently active texture, or we would risk
111 * marking already applied sampler states dirty again.
113 * TODO: Track the current active texture per GL context instead of using glGet
115 GLint active_texture;
116 ENTER_GL();
117 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
118 LEAVE_GL();
119 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
121 if (active_sampler != -1) {
122 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
124 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
127 /* This function checks if the primary render target uses the 8bit paletted format. */
128 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
130 if (device->render_targets && device->render_targets[0]) {
131 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
132 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
133 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
134 return TRUE;
136 return FALSE;
139 /* This call just downloads data, the caller is responsible for activating the
140 * right context and binding the correct texture. */
141 static void surface_download_data(IWineD3DSurfaceImpl *This) {
142 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
144 /* Only support read back of converted P8 surfaces */
145 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
147 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
148 return;
151 ENTER_GL();
153 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
154 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
155 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
157 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
158 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
159 } else {
160 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n",
161 This, This->glDescription.level, format_desc->glFormat, format_desc->glType,
162 This->resource.allocatedMemory);
164 if(This->Flags & SFLAG_PBO) {
165 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
166 checkGLcall("glBindBufferARB");
167 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
168 checkGLcall("glGetCompressedTexImageARB()");
169 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
170 checkGLcall("glBindBufferARB");
171 } else {
172 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
173 checkGLcall("glGetCompressedTexImageARB()");
176 LEAVE_GL();
177 } else {
178 void *mem;
179 GLenum format = format_desc->glFormat;
180 GLenum type = format_desc->glType;
181 int src_pitch = 0;
182 int dst_pitch = 0;
184 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
185 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
187 format = GL_ALPHA;
188 type = GL_UNSIGNED_BYTE;
191 if (This->Flags & SFLAG_NONPOW2) {
192 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
193 src_pitch = format_desc->byte_count * This->pow2Width;
194 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
195 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
196 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
197 } else {
198 mem = This->resource.allocatedMemory;
201 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
202 format, type, mem);
204 if(This->Flags & SFLAG_PBO) {
205 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
206 checkGLcall("glBindBufferARB");
208 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
209 type, NULL);
210 checkGLcall("glGetTexImage()");
212 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
213 checkGLcall("glBindBufferARB");
214 } else {
215 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
216 type, mem);
217 checkGLcall("glGetTexImage()");
219 LEAVE_GL();
221 if (This->Flags & SFLAG_NONPOW2) {
222 const BYTE *src_data;
223 BYTE *dst_data;
224 UINT y;
226 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
227 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
228 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
230 * We're doing this...
232 * instead of boxing the texture :
233 * |<-texture width ->| -->pow2width| /\
234 * |111111111111111111| | |
235 * |222 Texture 222222| boxed empty | texture height
236 * |3333 Data 33333333| | |
237 * |444444444444444444| | \/
238 * ----------------------------------- |
239 * | boxed empty | boxed empty | pow2height
240 * | | | \/
241 * -----------------------------------
244 * we're repacking the data to the expected texture width
246 * |<-texture width ->| -->pow2width| /\
247 * |111111111111111111222222222222222| |
248 * |222333333333333333333444444444444| texture height
249 * |444444 | |
250 * | | \/
251 * | | |
252 * | empty | pow2height
253 * | | \/
254 * -----------------------------------
256 * == is the same as
258 * |<-texture width ->| /\
259 * |111111111111111111|
260 * |222222222222222222|texture height
261 * |333333333333333333|
262 * |444444444444444444| \/
263 * --------------------
265 * this also means that any references to allocatedMemory should work with the data as if were a
266 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
268 * internally the texture is still stored in a boxed format so any references to textureName will
269 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
271 * Performance should not be an issue, because applications normally do not lock the surfaces when
272 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
273 * and doesn't have to be re-read.
275 src_data = mem;
276 dst_data = This->resource.allocatedMemory;
277 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
278 for (y = 1 ; y < This->currentDesc.Height; y++) {
279 /* skip the first row */
280 src_data += src_pitch;
281 dst_data += dst_pitch;
282 memcpy(dst_data, src_data, dst_pitch);
285 HeapFree(GetProcessHeap(), 0, mem);
289 /* Surface has now been downloaded */
290 This->Flags |= SFLAG_INSYSMEM;
293 /* This call just uploads data, the caller is responsible for activating the
294 * right context and binding the correct texture. */
295 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
296 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
298 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
300 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
301 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
302 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
304 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
305 FIXME("Using DXT1/3/5 without advertized support\n");
306 } else {
307 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
308 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
309 * function uses glCompressedTexImage2D instead of the SubImage call
311 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
312 ENTER_GL();
314 if(This->Flags & SFLAG_PBO) {
315 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
316 checkGLcall("glBindBufferARB");
317 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
319 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
320 width, height, 0 /* border */, This->resource.size, NULL));
321 checkGLcall("glCompressedTexSubImage2D");
323 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
324 checkGLcall("glBindBufferARB");
325 } else {
326 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
327 width, height, 0 /* border */, This->resource.size, data));
328 checkGLcall("glCompressedTexSubImage2D");
330 LEAVE_GL();
332 } else {
333 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
334 ENTER_GL();
336 if(This->Flags & SFLAG_PBO) {
337 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
338 checkGLcall("glBindBufferARB");
339 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
341 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
342 checkGLcall("glTexSubImage2D");
344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
345 checkGLcall("glBindBufferARB");
347 else {
348 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
349 checkGLcall("glTexSubImage2D");
352 LEAVE_GL();
356 /* This call just allocates the texture, the caller is responsible for
357 * activating the right context and binding the correct texture. */
358 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
359 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
360 BOOL enable_client_storage = FALSE;
361 const BYTE *mem = NULL;
363 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
365 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n",
366 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
367 internal, width, height, format, type);
369 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
370 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
371 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
373 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
374 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
376 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
377 * once, unfortunately
379 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
380 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
381 This->Flags |= SFLAG_CLIENT;
382 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
383 ENTER_GL();
384 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
385 width, height, 0 /* border */, This->resource.size, mem));
386 LEAVE_GL();
389 return;
392 ENTER_GL();
394 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
395 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
396 /* In some cases we want to disable client storage.
397 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
398 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
399 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
400 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
401 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
403 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
404 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
405 This->Flags &= ~SFLAG_CLIENT;
406 enable_client_storage = TRUE;
407 } else {
408 This->Flags |= SFLAG_CLIENT;
410 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
411 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
413 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
416 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
417 checkGLcall("glTexImage2D");
419 if(enable_client_storage) {
420 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
421 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
423 LEAVE_GL();
426 /* In D3D the depth stencil dimensions have to be greater than or equal to the
427 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
428 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
429 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
430 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
431 renderbuffer_entry_t *entry;
432 GLuint renderbuffer = 0;
433 unsigned int src_width, src_height;
435 src_width = This->pow2Width;
436 src_height = This->pow2Height;
438 /* A depth stencil smaller than the render target is not valid */
439 if (width > src_width || height > src_height) return;
441 /* Remove any renderbuffer set if the sizes match */
442 if (width == src_width && height == src_height) {
443 This->current_renderbuffer = NULL;
444 return;
447 /* Look if we've already got a renderbuffer of the correct dimensions */
448 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
449 if (entry->width == width && entry->height == height) {
450 renderbuffer = entry->id;
451 This->current_renderbuffer = entry;
452 break;
456 if (!renderbuffer) {
457 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
458 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
459 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
460 This->resource.format_desc->glInternal, width, height));
462 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
463 entry->width = width;
464 entry->height = height;
465 entry->id = renderbuffer;
466 list_add_head(&This->renderbuffers, &entry->entry);
468 This->current_renderbuffer = entry;
471 checkGLcall("set_compatible_renderbuffer");
474 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
475 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
476 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
478 TRACE("(%p) : swapchain %p\n", This, swapchain);
480 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
481 TRACE("Returning GL_BACK\n");
482 return GL_BACK;
483 } else if (swapchain_impl->frontBuffer == iface) {
484 TRACE("Returning GL_FRONT\n");
485 return GL_FRONT;
488 FIXME("Higher back buffer, returning GL_BACK\n");
489 return GL_BACK;
492 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
493 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
496 IWineD3DBaseTexture *baseTexture = NULL;
498 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
499 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
501 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
502 if (dirty_rect)
504 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
505 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
506 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
507 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
509 else
511 This->dirtyRect.left = 0;
512 This->dirtyRect.top = 0;
513 This->dirtyRect.right = This->currentDesc.Width;
514 This->dirtyRect.bottom = This->currentDesc.Height;
517 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
518 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
520 /* if the container is a basetexture then mark it dirty. */
521 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
523 TRACE("Passing to container\n");
524 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
525 IWineD3DBaseTexture_Release(baseTexture);
529 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
531 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
532 ULONG ref = InterlockedDecrement(&This->resource.ref);
533 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
534 if (ref == 0) {
535 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
536 renderbuffer_entry_t *entry, *entry2;
537 TRACE("(%p) : cleaning up\n", This);
539 /* Need a context to destroy the texture. Use the currently active render target, but only if
540 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
541 * When destroying the primary rt, Uninit3D will activate a context before doing anything
543 if(device->render_targets && device->render_targets[0]) {
544 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
547 ENTER_GL();
548 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
549 TRACE("Deleting texture %d\n", This->glDescription.textureName);
550 glDeleteTextures(1, &This->glDescription.textureName);
553 if(This->Flags & SFLAG_PBO) {
554 /* Delete the PBO */
555 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
558 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
559 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
560 HeapFree(GetProcessHeap(), 0, entry);
562 LEAVE_GL();
564 if(This->Flags & SFLAG_DIBSECTION) {
565 /* Release the DC */
566 SelectObject(This->hDC, This->dib.holdbitmap);
567 DeleteDC(This->hDC);
568 /* Release the DIB section */
569 DeleteObject(This->dib.DIBsection);
570 This->dib.bitmap_data = NULL;
571 This->resource.allocatedMemory = NULL;
573 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
575 HeapFree(GetProcessHeap(), 0, This->palette9);
577 resource_cleanup((IWineD3DResource *)iface);
579 if(This->overlay_dest) {
580 list_remove(&This->overlay_entry);
583 TRACE("(%p) Released\n", This);
584 HeapFree(GetProcessHeap(), 0, This);
587 return ref;
590 /* ****************************************************
591 IWineD3DSurface IWineD3DResource parts follow
592 **************************************************** */
594 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
596 /* TODO: check for locks */
597 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
598 IWineD3DBaseTexture *baseTexture = NULL;
599 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
601 TRACE("(%p)Checking to see if the container is a base texture\n", This);
602 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
603 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
604 TRACE("Passing to container\n");
605 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
606 IWineD3DBaseTexture_Release(baseTexture);
607 } else {
608 TRACE("(%p) : About to load surface\n", This);
610 if(!device->isInDraw) {
611 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
614 if (This->resource.format_desc->format == WINED3DFMT_P8
615 || This->resource.format_desc->format == WINED3DFMT_A8P8)
617 if(palette9_changed(This)) {
618 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
619 /* TODO: This is not necessarily needed with hw palettized texture support */
620 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
621 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
622 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
626 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
628 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
629 /* Tell opengl to try and keep this texture in video ram (well mostly) */
630 GLclampf tmp;
631 tmp = 0.9f;
632 ENTER_GL();
633 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
634 LEAVE_GL();
637 return;
640 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
641 surface_internal_preload(iface, SRGB_ANY);
644 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
645 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
646 This->resource.allocatedMemory =
647 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
649 ENTER_GL();
650 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
651 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
652 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
653 checkGLcall("glGetBufferSubData");
654 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
655 checkGLcall("glDeleteBuffers");
656 LEAVE_GL();
658 This->pbo = 0;
659 This->Flags &= ~SFLAG_PBO;
662 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
663 IWineD3DBaseTexture *texture = NULL;
664 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
665 renderbuffer_entry_t *entry, *entry2;
666 TRACE("(%p)\n", iface);
668 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
669 /* Default pool resources are supposed to be destroyed before Reset is called.
670 * Implicit resources stay however. So this means we have an implicit render target
671 * or depth stencil. The content may be destroyed, but we still have to tear down
672 * opengl resources, so we cannot leave early.
674 * Put the most up to date surface location into the drawable. D3D-wise this content
675 * is undefined, so it would be nowhere, but that would make the location management
676 * more complicated. The drawable is a sane location, because if we mark sysmem or
677 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
678 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
679 * sysmem copy here.
681 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
682 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
683 } else {
684 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
686 } else {
687 /* Load the surface into system memory */
688 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
689 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
691 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
692 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
694 /* Destroy PBOs, but load them into real sysmem before */
695 if(This->Flags & SFLAG_PBO) {
696 surface_remove_pbo(This);
699 /* Destroy fbo render buffers. This is needed for implicit render targets, for
700 * all application-created targets the application has to release the surface
701 * before calling _Reset
703 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
704 ENTER_GL();
705 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
706 LEAVE_GL();
707 list_remove(&entry->entry);
708 HeapFree(GetProcessHeap(), 0, entry);
710 list_init(&This->renderbuffers);
711 This->current_renderbuffer = NULL;
713 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
714 * destroy it
716 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
717 if(!texture) {
718 ENTER_GL();
719 glDeleteTextures(1, &This->glDescription.textureName);
720 This->glDescription.textureName = 0;
721 LEAVE_GL();
722 } else {
723 IWineD3DBaseTexture_Release(texture);
725 return;
728 /* ******************************************************
729 IWineD3DSurface IWineD3DSurface parts follow
730 ****************************************************** */
732 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
734 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
735 TRACE("(%p) : returning %p\n", This, &This->glDescription);
736 *glDescription = &This->glDescription;
739 /* Read the framebuffer back into the surface */
740 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
741 IWineD3DSwapChainImpl *swapchain;
742 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
743 BYTE *mem;
744 GLint fmt;
745 GLint type;
746 BYTE *row, *top, *bottom;
747 int i;
748 BOOL bpp;
749 RECT local_rect;
750 BOOL srcIsUpsideDown;
751 GLint rowLen = 0;
752 GLint skipPix = 0;
753 GLint skipRow = 0;
755 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
756 static BOOL warned = FALSE;
757 if(!warned) {
758 ERR("The application tries to lock the render target, but render target locking is disabled\n");
759 warned = TRUE;
761 return;
764 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
765 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
766 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
767 * context->last_was_blit set on the unlock.
769 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
770 ENTER_GL();
772 /* Select the correct read buffer, and give some debug output.
773 * There is no need to keep track of the current read buffer or reset it, every part of the code
774 * that reads sets the read buffer as desired.
776 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
778 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
779 TRACE("Locking %#x buffer\n", buffer);
780 glReadBuffer(buffer);
781 checkGLcall("glReadBuffer");
783 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
784 srcIsUpsideDown = FALSE;
785 } else {
786 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
787 * Read from the back buffer
789 TRACE("Locking offscreen render target\n");
790 glReadBuffer(myDevice->offscreenBuffer);
791 srcIsUpsideDown = TRUE;
794 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
795 if(!rect) {
796 local_rect.left = 0;
797 local_rect.top = 0;
798 local_rect.right = This->currentDesc.Width;
799 local_rect.bottom = This->currentDesc.Height;
800 } else {
801 local_rect = *rect;
803 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
805 switch(This->resource.format_desc->format)
807 case WINED3DFMT_P8:
809 if(primary_render_target_is_p8(myDevice)) {
810 /* In case of P8 render targets the index is stored in the alpha component */
811 fmt = GL_ALPHA;
812 type = GL_UNSIGNED_BYTE;
813 mem = dest;
814 bpp = This->resource.format_desc->byte_count;
815 } else {
816 /* GL can't return palettized data, so read ARGB pixels into a
817 * separate block of memory and convert them into palettized format
818 * in software. Slow, but if the app means to use palettized render
819 * targets and locks it...
821 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
822 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
823 * for the color channels when palettizing the colors.
825 fmt = GL_RGB;
826 type = GL_UNSIGNED_BYTE;
827 pitch *= 3;
828 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
829 if(!mem) {
830 ERR("Out of memory\n");
831 LEAVE_GL();
832 return;
834 bpp = This->resource.format_desc->byte_count * 3;
837 break;
839 default:
840 mem = dest;
841 fmt = This->resource.format_desc->glFormat;
842 type = This->resource.format_desc->glType;
843 bpp = This->resource.format_desc->byte_count;
846 if(This->Flags & SFLAG_PBO) {
847 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
848 checkGLcall("glBindBufferARB");
849 if(mem != NULL) {
850 ERR("mem not null for pbo -- unexpected\n");
851 mem = NULL;
855 /* Save old pixel store pack state */
856 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
857 checkGLcall("glIntegerv");
858 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
859 checkGLcall("glIntegerv");
860 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
861 checkGLcall("glIntegerv");
863 /* Setup pixel store pack state -- to glReadPixels into the correct place */
864 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
865 checkGLcall("glPixelStorei");
866 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
867 checkGLcall("glPixelStorei");
868 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
869 checkGLcall("glPixelStorei");
871 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
872 local_rect.right - local_rect.left,
873 local_rect.bottom - local_rect.top,
874 fmt, type, mem);
875 checkGLcall("glReadPixels");
877 /* Reset previous pixel store pack state */
878 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
879 checkGLcall("glPixelStorei");
880 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
881 checkGLcall("glPixelStorei");
882 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
883 checkGLcall("glPixelStorei");
885 if(This->Flags & SFLAG_PBO) {
886 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
887 checkGLcall("glBindBufferARB");
889 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
890 * to get a pointer to it and perform the flipping in software. This is a lot
891 * faster than calling glReadPixels for each line. In case we want more speed
892 * we should rerender it flipped in a FBO and read the data back from the FBO. */
893 if(!srcIsUpsideDown) {
894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
895 checkGLcall("glBindBufferARB");
897 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
898 checkGLcall("glMapBufferARB");
902 /* TODO: Merge this with the palettization loop below for P8 targets */
903 if(!srcIsUpsideDown) {
904 UINT len, off;
905 /* glReadPixels returns the image upside down, and there is no way to prevent this.
906 Flip the lines in software */
907 len = (local_rect.right - local_rect.left) * bpp;
908 off = local_rect.left * bpp;
910 row = HeapAlloc(GetProcessHeap(), 0, len);
911 if(!row) {
912 ERR("Out of memory\n");
913 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
914 LEAVE_GL();
915 return;
918 top = mem + pitch * local_rect.top;
919 bottom = mem + pitch * (local_rect.bottom - 1);
920 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
921 memcpy(row, top + off, len);
922 memcpy(top + off, bottom + off, len);
923 memcpy(bottom + off, row, len);
924 top += pitch;
925 bottom -= pitch;
927 HeapFree(GetProcessHeap(), 0, row);
929 /* Unmap the temp PBO buffer */
930 if(This->Flags & SFLAG_PBO) {
931 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
932 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
936 LEAVE_GL();
938 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
939 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
940 * the same color but we have no choice.
941 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
943 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
945 const PALETTEENTRY *pal = NULL;
946 DWORD width = pitch / 3;
947 int x, y, c;
949 if(This->palette) {
950 pal = This->palette->palents;
951 } else {
952 ERR("Palette is missing, cannot perform inverse palette lookup\n");
953 HeapFree(GetProcessHeap(), 0, mem);
954 return ;
957 for(y = local_rect.top; y < local_rect.bottom; y++) {
958 for(x = local_rect.left; x < local_rect.right; x++) {
959 /* start lines pixels */
960 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
961 const BYTE *green = blue + 1;
962 const BYTE *red = green + 1;
964 for(c = 0; c < 256; c++) {
965 if(*red == pal[c].peRed &&
966 *green == pal[c].peGreen &&
967 *blue == pal[c].peBlue)
969 *((BYTE *) dest + y * width + x) = c;
970 break;
975 HeapFree(GetProcessHeap(), 0, mem);
979 /* Read the framebuffer contents into a texture */
980 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
982 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
983 IWineD3DSwapChainImpl *swapchain;
984 int bpp;
985 GLenum format, internal, type;
986 CONVERT_TYPES convert;
987 GLint prevRead;
988 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
990 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
992 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
993 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
994 * states in the stateblock, and no driver was found yet that had bugs in that regard.
996 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
997 surface_bind_and_dirtify(This, srgb);
999 ENTER_GL();
1000 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1001 LEAVE_GL();
1003 /* Select the correct read buffer, and give some debug output.
1004 * There is no need to keep track of the current read buffer or reset it, every part of the code
1005 * that reads sets the read buffer as desired.
1007 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1009 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1010 TRACE("Locking %#x buffer\n", buffer);
1012 ENTER_GL();
1013 glReadBuffer(buffer);
1014 checkGLcall("glReadBuffer");
1015 LEAVE_GL();
1017 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1018 } else {
1019 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1020 * Read from the back buffer
1022 TRACE("Locking offscreen render target\n");
1023 ENTER_GL();
1024 glReadBuffer(device->offscreenBuffer);
1025 checkGLcall("glReadBuffer");
1026 LEAVE_GL();
1029 if(!(This->Flags & alloc_flag)) {
1030 surface_allocate_surface(This, internal, This->pow2Width,
1031 This->pow2Height, format, type);
1032 This->Flags |= alloc_flag;
1035 ENTER_GL();
1036 /* If !SrcIsUpsideDown we should flip the surface.
1037 * This can be done using glCopyTexSubImage2D but this
1038 * is VERY slow, so don't do that. We should prevent
1039 * this code from getting called in such cases or perhaps
1040 * we can use FBOs */
1042 glCopyTexSubImage2D(This->glDescription.target,
1043 This->glDescription.level,
1044 0, 0, 0, 0,
1045 This->currentDesc.Width,
1046 This->currentDesc.Height);
1047 checkGLcall("glCopyTexSubImage2D");
1049 glReadBuffer(prevRead);
1050 checkGLcall("glReadBuffer");
1052 LEAVE_GL();
1053 TRACE("Updated target %d\n", This->glDescription.target);
1056 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1057 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1058 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1059 * changed
1061 if(!(This->Flags & SFLAG_DYNLOCK)) {
1062 This->lockCount++;
1063 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1064 if(This->lockCount > MAXLOCKCOUNT) {
1065 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1066 This->Flags |= SFLAG_DYNLOCK;
1070 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1071 * Also don't create a PBO for systemmem surfaces.
1073 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1074 GLenum error;
1075 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1077 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1078 ENTER_GL();
1080 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1081 error = glGetError();
1082 if(This->pbo == 0 || error != GL_NO_ERROR) {
1083 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1086 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1088 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1089 checkGLcall("glBindBufferARB");
1091 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1092 checkGLcall("glBufferDataARB");
1094 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1095 checkGLcall("glBindBufferARB");
1097 /* We don't need the system memory anymore and we can't even use it for PBOs */
1098 if(!(This->Flags & SFLAG_CLIENT)) {
1099 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1100 This->resource.heapMemory = NULL;
1102 This->resource.allocatedMemory = NULL;
1103 This->Flags |= SFLAG_PBO;
1104 LEAVE_GL();
1105 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1106 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1107 * or a pbo to map
1109 if(!This->resource.heapMemory) {
1110 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1112 This->resource.allocatedMemory =
1113 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1114 if(This->Flags & SFLAG_INSYSMEM) {
1115 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1120 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1121 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1122 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1124 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1126 /* This is also done in the base class, but we have to verify this before loading any data from
1127 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1128 * may interfere, and all other bad things may happen
1130 if (This->Flags & SFLAG_LOCKED) {
1131 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1132 return WINED3DERR_INVALIDCALL;
1134 This->Flags |= SFLAG_LOCKED;
1136 if (!(This->Flags & SFLAG_LOCKABLE))
1138 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1141 if (Flags & WINED3DLOCK_DISCARD) {
1142 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1143 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1144 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1145 This->Flags |= SFLAG_INSYSMEM;
1146 goto lock_end;
1149 if (This->Flags & SFLAG_INSYSMEM) {
1150 TRACE("Local copy is up to date, not downloading data\n");
1151 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1152 goto lock_end;
1155 /* Now download the surface content from opengl
1156 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1157 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1159 if ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])
1161 const RECT *pass_rect = pRect;
1163 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1164 * because most caller functions do not need that. So do that here
1166 if(pRect &&
1167 pRect->top == 0 &&
1168 pRect->left == 0 &&
1169 pRect->right == This->currentDesc.Width &&
1170 pRect->bottom == This->currentDesc.Height) {
1171 pass_rect = NULL;
1174 switch(wined3d_settings.rendertargetlock_mode) {
1175 case RTL_TEXDRAW:
1176 case RTL_TEXTEX:
1177 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1178 #if 0
1179 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1180 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1181 * This may be faster on some cards
1183 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1184 #endif
1185 /* drop through */
1187 case RTL_AUTO:
1188 case RTL_READDRAW:
1189 case RTL_READTEX:
1190 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1191 break;
1193 case RTL_DISABLE:
1194 break;
1196 } else if(iface == myDevice->stencilBufferTarget) {
1197 /** the depth stencil in openGL has a format of GL_FLOAT
1198 * which should be good for WINED3DFMT_D16_LOCKABLE
1199 * and WINED3DFMT_D16
1200 * it is unclear what format the stencil buffer is in except.
1201 * 'Each index is converted to fixed point...
1202 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1203 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1204 * glReadPixels(This->lockedRect.left,
1205 * This->lockedRect.bottom - j - 1,
1206 * This->lockedRect.right - This->lockedRect.left,
1207 * 1,
1208 * GL_DEPTH_COMPONENT,
1209 * type,
1210 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1212 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1213 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1214 * none of that is the case the problem is not in this function :-)
1215 ********************************************/
1216 FIXME("Depth stencil locking not supported yet\n");
1217 } else {
1218 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1219 TRACE("locking an ordinary surface\n");
1220 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1223 lock_end:
1224 if(This->Flags & SFLAG_PBO) {
1225 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1226 ENTER_GL();
1227 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1228 checkGLcall("glBindBufferARB");
1230 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1231 if(This->resource.allocatedMemory) {
1232 ERR("The surface already has PBO memory allocated!\n");
1235 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1236 checkGLcall("glMapBufferARB");
1238 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1240 checkGLcall("glBindBufferARB");
1242 LEAVE_GL();
1245 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1246 /* Don't dirtify */
1247 } else {
1248 IWineD3DBaseTexture *pBaseTexture;
1250 * Dirtify on lock
1251 * as seen in msdn docs
1253 surface_add_dirty_rect(iface, pRect);
1255 /** Dirtify Container if needed */
1256 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1257 TRACE("Making container dirty\n");
1258 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1259 IWineD3DBaseTexture_Release(pBaseTexture);
1260 } else {
1261 TRACE("Surface is standalone, no need to dirty the container\n");
1265 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1268 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1269 GLint prev_store;
1270 GLint prev_rasterpos[4];
1271 GLint skipBytes = 0;
1272 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1273 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1274 IWineD3DSwapChainImpl *swapchain;
1276 /* Activate the correct context for the render target */
1277 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1278 ENTER_GL();
1280 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1281 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1282 TRACE("Unlocking %#x buffer\n", buffer);
1283 glDrawBuffer(buffer);
1284 checkGLcall("glDrawBuffer");
1286 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1287 } else {
1288 /* Primary offscreen render target */
1289 TRACE("Offscreen render target\n");
1290 glDrawBuffer(myDevice->offscreenBuffer);
1291 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1294 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1295 checkGLcall("glIntegerv");
1296 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1297 checkGLcall("glIntegerv");
1298 glPixelZoom(1.0, -1.0);
1299 checkGLcall("glPixelZoom");
1301 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1302 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1303 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1305 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1306 checkGLcall("glRasterPos2f");
1308 /* Some drivers(radeon dri, others?) don't like exceptions during
1309 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1310 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1311 * catch to put the dib section in InSync mode, which leads to a crash
1312 * and a blocked x server on my radeon card.
1314 * The following lines read the dib section so it is put in InSync mode
1315 * before glDrawPixels is called and the crash is prevented. There won't
1316 * be any interfering gdi accesses, because UnlockRect is called from
1317 * ReleaseDC, and the app won't use the dc any more afterwards.
1319 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1320 volatile BYTE read;
1321 read = This->resource.allocatedMemory[0];
1324 if(This->Flags & SFLAG_PBO) {
1325 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1326 checkGLcall("glBindBufferARB");
1329 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1330 if(This->Flags & SFLAG_LOCKED) {
1331 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1332 (This->lockedRect.bottom - This->lockedRect.top)-1,
1333 fmt, type,
1334 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1335 checkGLcall("glDrawPixels");
1336 } else {
1337 glDrawPixels(This->currentDesc.Width,
1338 This->currentDesc.Height,
1339 fmt, type, mem);
1340 checkGLcall("glDrawPixels");
1343 if(This->Flags & SFLAG_PBO) {
1344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1345 checkGLcall("glBindBufferARB");
1348 glPixelZoom(1.0,1.0);
1349 checkGLcall("glPixelZoom");
1351 glRasterPos3iv(&prev_rasterpos[0]);
1352 checkGLcall("glRasterPos3iv");
1354 /* Reset to previous pack row length */
1355 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1356 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1358 if(!swapchain) {
1359 glDrawBuffer(myDevice->offscreenBuffer);
1360 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1361 } else if(swapchain->backBuffer) {
1362 glDrawBuffer(GL_BACK);
1363 checkGLcall("glDrawBuffer(GL_BACK)");
1364 } else {
1365 glDrawBuffer(GL_FRONT);
1366 checkGLcall("glDrawBuffer(GL_FRONT)");
1368 LEAVE_GL();
1370 return;
1373 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1374 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1375 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1376 BOOL fullsurface;
1378 if (!(This->Flags & SFLAG_LOCKED)) {
1379 WARN("trying to Unlock an unlocked surf@%p\n", This);
1380 return WINEDDERR_NOTLOCKED;
1383 if (This->Flags & SFLAG_PBO) {
1384 TRACE("Freeing PBO memory\n");
1385 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1386 ENTER_GL();
1387 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1388 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1389 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1390 checkGLcall("glUnmapBufferARB");
1391 LEAVE_GL();
1392 This->resource.allocatedMemory = NULL;
1395 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1397 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1398 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1399 goto unlock_end;
1402 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1404 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1405 static BOOL warned = FALSE;
1406 if(!warned) {
1407 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1408 warned = TRUE;
1410 goto unlock_end;
1413 if(This->dirtyRect.left == 0 &&
1414 This->dirtyRect.top == 0 &&
1415 This->dirtyRect.right == This->currentDesc.Width &&
1416 This->dirtyRect.bottom == This->currentDesc.Height) {
1417 fullsurface = TRUE;
1418 } else {
1419 /* TODO: Proper partial rectangle tracking */
1420 fullsurface = FALSE;
1421 This->Flags |= SFLAG_INSYSMEM;
1424 switch(wined3d_settings.rendertargetlock_mode) {
1425 case RTL_READTEX:
1426 case RTL_TEXTEX:
1427 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1428 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1429 /* drop through */
1431 case RTL_AUTO:
1432 case RTL_READDRAW:
1433 case RTL_TEXDRAW:
1434 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1435 break;
1438 if(!fullsurface) {
1439 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1440 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1441 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1442 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1443 * not fully up to date because only a subrectangle was read in LockRect.
1445 This->Flags &= ~SFLAG_INSYSMEM;
1446 This->Flags |= SFLAG_INDRAWABLE;
1449 This->dirtyRect.left = This->currentDesc.Width;
1450 This->dirtyRect.top = This->currentDesc.Height;
1451 This->dirtyRect.right = 0;
1452 This->dirtyRect.bottom = 0;
1453 } else if(iface == myDevice->stencilBufferTarget) {
1454 FIXME("Depth Stencil buffer locking is not implemented\n");
1455 } else {
1456 /* The rest should be a normal texture */
1457 IWineD3DBaseTextureImpl *impl;
1458 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1459 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1460 * states need resetting
1462 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1463 if(impl->baseTexture.bindCount) {
1464 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1466 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1470 unlock_end:
1471 This->Flags &= ~SFLAG_LOCKED;
1472 memset(&This->lockedRect, 0, sizeof(RECT));
1474 /* Overlays have to be redrawn manually after changes with the GL implementation */
1475 if(This->overlay_dest) {
1476 IWineD3DSurface_DrawOverlay(iface);
1478 return WINED3D_OK;
1481 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1483 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1484 WINED3DLOCKED_RECT lock;
1485 HRESULT hr;
1486 RGBQUAD col[256];
1488 TRACE("(%p)->(%p)\n",This,pHDC);
1490 if(This->Flags & SFLAG_USERPTR) {
1491 ERR("Not supported on surfaces with an application-provided surfaces\n");
1492 return WINEDDERR_NODC;
1495 /* Give more detailed info for ddraw */
1496 if (This->Flags & SFLAG_DCINUSE)
1497 return WINEDDERR_DCALREADYCREATED;
1499 /* Can't GetDC if the surface is locked */
1500 if (This->Flags & SFLAG_LOCKED)
1501 return WINED3DERR_INVALIDCALL;
1503 /* According to Direct3D9 docs, only these formats are supported */
1504 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1505 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1506 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1507 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1508 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1509 return WINED3DERR_INVALIDCALL;
1512 memset(&lock, 0, sizeof(lock)); /* To be sure */
1514 /* Create a DIB section if there isn't a hdc yet */
1515 if(!This->hDC) {
1516 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1517 if(This->Flags & SFLAG_CLIENT) {
1518 surface_internal_preload(iface, SRGB_RGB);
1521 /* Use the dib section from now on if we are not using a PBO */
1522 if(!(This->Flags & SFLAG_PBO))
1523 This->resource.allocatedMemory = This->dib.bitmap_data;
1526 /* Lock the surface */
1527 hr = IWineD3DSurface_LockRect(iface,
1528 &lock,
1529 NULL,
1532 if(This->Flags & SFLAG_PBO) {
1533 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1534 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1537 if(FAILED(hr)) {
1538 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1539 /* keep the dib section */
1540 return hr;
1543 if (This->resource.format_desc->format == WINED3DFMT_P8
1544 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1546 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1547 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1548 unsigned int n;
1549 const PALETTEENTRY *pal = NULL;
1551 if(This->palette) {
1552 pal = This->palette->palents;
1553 } else {
1554 IWineD3DSurfaceImpl *dds_primary;
1555 IWineD3DSwapChainImpl *swapchain;
1556 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1557 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1558 if (dds_primary && dds_primary->palette)
1559 pal = dds_primary->palette->palents;
1562 if (pal) {
1563 for (n=0; n<256; n++) {
1564 col[n].rgbRed = pal[n].peRed;
1565 col[n].rgbGreen = pal[n].peGreen;
1566 col[n].rgbBlue = pal[n].peBlue;
1567 col[n].rgbReserved = 0;
1569 SetDIBColorTable(This->hDC, 0, 256, col);
1573 *pHDC = This->hDC;
1574 TRACE("returning %p\n",*pHDC);
1575 This->Flags |= SFLAG_DCINUSE;
1577 return WINED3D_OK;
1580 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1582 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1584 TRACE("(%p)->(%p)\n",This,hDC);
1586 if (!(This->Flags & SFLAG_DCINUSE))
1587 return WINEDDERR_NODC;
1589 if (This->hDC !=hDC) {
1590 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1591 return WINEDDERR_NODC;
1594 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1595 /* Copy the contents of the DIB over to the PBO */
1596 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1599 /* we locked first, so unlock now */
1600 IWineD3DSurface_UnlockRect(iface);
1602 This->Flags &= ~SFLAG_DCINUSE;
1604 return WINED3D_OK;
1607 /* ******************************************************
1608 IWineD3DSurface Internal (No mapping to directx api) parts follow
1609 ****************************************************** */
1611 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) {
1612 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1613 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1614 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1616 /* Default values: From the surface */
1617 *format = glDesc->glFormat;
1618 *type = glDesc->glType;
1619 *convert = NO_CONVERSION;
1620 *target_bpp = glDesc->byte_count;
1622 if(srgb_mode) {
1623 *internal = glDesc->glGammaInternal;
1625 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1626 && !(This->Flags & SFLAG_SWAPCHAIN))
1628 *internal = glDesc->rtInternal;
1629 } else {
1630 *internal = glDesc->glInternal;
1633 /* Ok, now look if we have to do any conversion */
1634 switch(This->resource.format_desc->format)
1636 case WINED3DFMT_P8:
1637 /* ****************
1638 Paletted Texture
1639 **************** */
1641 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1642 * of the two is available make sure texturing is requested as neither of the two works in
1643 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1644 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1645 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1646 * conflicts with this.
1648 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1649 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1650 device->render_targets &&
1651 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1652 colorkey_active || !use_texturing ) {
1653 *format = GL_RGBA;
1654 *internal = GL_RGBA;
1655 *type = GL_UNSIGNED_BYTE;
1656 *target_bpp = 4;
1657 if(colorkey_active) {
1658 *convert = CONVERT_PALETTED_CK;
1659 } else {
1660 *convert = CONVERT_PALETTED;
1663 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1664 *format = GL_ALPHA;
1665 *internal = GL_RGBA;
1666 *type = GL_UNSIGNED_BYTE;
1667 *target_bpp = 1;
1670 break;
1672 case WINED3DFMT_R3G3B2:
1673 /* **********************
1674 GL_UNSIGNED_BYTE_3_3_2
1675 ********************** */
1676 if (colorkey_active) {
1677 /* This texture format will never be used.. So do not care about color keying
1678 up until the point in time it will be needed :-) */
1679 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1681 break;
1683 case WINED3DFMT_R5G6B5:
1684 if (colorkey_active) {
1685 *convert = CONVERT_CK_565;
1686 *format = GL_RGBA;
1687 *internal = GL_RGBA;
1688 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1690 break;
1692 case WINED3DFMT_X1R5G5B5:
1693 if (colorkey_active) {
1694 *convert = CONVERT_CK_5551;
1695 *format = GL_BGRA;
1696 *internal = GL_RGBA;
1697 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1699 break;
1701 case WINED3DFMT_R8G8B8:
1702 if (colorkey_active) {
1703 *convert = CONVERT_CK_RGB24;
1704 *format = GL_RGBA;
1705 *internal = GL_RGBA;
1706 *type = GL_UNSIGNED_INT_8_8_8_8;
1707 *target_bpp = 4;
1709 break;
1711 case WINED3DFMT_X8R8G8B8:
1712 if (colorkey_active) {
1713 *convert = CONVERT_RGB32_888;
1714 *format = GL_RGBA;
1715 *internal = GL_RGBA;
1716 *type = GL_UNSIGNED_INT_8_8_8_8;
1718 break;
1720 case WINED3DFMT_R8G8_SNORM:
1721 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1722 *convert = CONVERT_V8U8;
1723 *format = GL_BGR;
1724 *internal = GL_RGB8;
1725 *type = GL_UNSIGNED_BYTE;
1726 *target_bpp = 3;
1727 break;
1729 case WINED3DFMT_L6V5U5:
1730 *convert = CONVERT_L6V5U5;
1731 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1732 *target_bpp = 3;
1733 /* Use format and types from table */
1734 } else {
1735 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1736 *target_bpp = 2;
1737 *format = GL_RGB;
1738 *internal = GL_RGB5;
1739 *type = GL_UNSIGNED_SHORT_5_6_5;
1741 break;
1743 case WINED3DFMT_X8L8V8U8:
1744 *convert = CONVERT_X8L8V8U8;
1745 *target_bpp = 4;
1746 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1747 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1748 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1749 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1750 * the needed type and format parameter, so the internal format contains a
1751 * 4th component, which is returned as alpha
1753 } else {
1754 *format = GL_BGRA;
1755 *internal = GL_RGB8;
1756 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1758 break;
1760 case WINED3DFMT_R8G8B8A8_SNORM:
1761 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1762 *convert = CONVERT_Q8W8V8U8;
1763 *format = GL_BGRA;
1764 *internal = GL_RGBA8;
1765 *type = GL_UNSIGNED_BYTE;
1766 *target_bpp = 4;
1767 break;
1769 case WINED3DFMT_R16G16_SNORM:
1770 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1771 *convert = CONVERT_V16U16;
1772 *format = GL_BGR;
1773 *internal = GL_RGB16_EXT;
1774 *type = GL_UNSIGNED_SHORT;
1775 *target_bpp = 6;
1776 break;
1778 case WINED3DFMT_A4L4:
1779 /* A4L4 exists as an internal gl format, but for some reason there is not
1780 * format+type combination to load it. Thus convert it to A8L8, then load it
1781 * with A4L4 internal, but A8L8 format+type
1783 *convert = CONVERT_A4L4;
1784 *format = GL_LUMINANCE_ALPHA;
1785 *internal = GL_LUMINANCE4_ALPHA4;
1786 *type = GL_UNSIGNED_BYTE;
1787 *target_bpp = 2;
1788 break;
1790 case WINED3DFMT_R16G16_UNORM:
1791 *convert = CONVERT_G16R16;
1792 *format = GL_RGB;
1793 *internal = GL_RGB16_EXT;
1794 *type = GL_UNSIGNED_SHORT;
1795 *target_bpp = 6;
1796 break;
1798 case WINED3DFMT_R16G16_FLOAT:
1799 *convert = CONVERT_R16G16F;
1800 *format = GL_RGB;
1801 *internal = GL_RGB16F_ARB;
1802 *type = GL_HALF_FLOAT_ARB;
1803 *target_bpp = 6;
1804 break;
1806 case WINED3DFMT_R32G32_FLOAT:
1807 *convert = CONVERT_R32G32F;
1808 *format = GL_RGB;
1809 *internal = GL_RGB32F_ARB;
1810 *type = GL_FLOAT;
1811 *target_bpp = 12;
1812 break;
1814 default:
1815 break;
1818 return WINED3D_OK;
1821 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1822 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1824 const BYTE *source;
1825 BYTE *dest;
1826 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1828 switch (convert) {
1829 case NO_CONVERSION:
1831 memcpy(dst, src, pitch * height);
1832 break;
1834 case CONVERT_PALETTED:
1835 case CONVERT_PALETTED_CK:
1837 IWineD3DPaletteImpl* pal = This->palette;
1838 BYTE table[256][4];
1839 unsigned int x, y;
1841 if( pal == NULL) {
1842 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1845 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1847 for (y = 0; y < height; y++)
1849 source = src + pitch * y;
1850 dest = dst + outpitch * y;
1851 /* This is an 1 bpp format, using the width here is fine */
1852 for (x = 0; x < width; x++) {
1853 BYTE color = *source++;
1854 *dest++ = table[color][0];
1855 *dest++ = table[color][1];
1856 *dest++ = table[color][2];
1857 *dest++ = table[color][3];
1861 break;
1863 case CONVERT_CK_565:
1865 /* Converting the 565 format in 5551 packed to emulate color-keying.
1867 Note : in all these conversion, it would be best to average the averaging
1868 pixels to get the color of the pixel that will be color-keyed to
1869 prevent 'color bleeding'. This will be done later on if ever it is
1870 too visible.
1872 Note2: Nvidia documents say that their driver does not support alpha + color keying
1873 on the same surface and disables color keying in such a case
1875 unsigned int x, y;
1876 const WORD *Source;
1877 WORD *Dest;
1879 TRACE("Color keyed 565\n");
1881 for (y = 0; y < height; y++) {
1882 Source = (const WORD *)(src + y * pitch);
1883 Dest = (WORD *) (dst + y * outpitch);
1884 for (x = 0; x < width; x++ ) {
1885 WORD color = *Source++;
1886 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1887 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1888 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1889 *Dest |= 0x0001;
1891 Dest++;
1895 break;
1897 case CONVERT_CK_5551:
1899 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1900 unsigned int x, y;
1901 const WORD *Source;
1902 WORD *Dest;
1903 TRACE("Color keyed 5551\n");
1904 for (y = 0; y < height; y++) {
1905 Source = (const WORD *)(src + y * pitch);
1906 Dest = (WORD *) (dst + y * outpitch);
1907 for (x = 0; x < width; x++ ) {
1908 WORD color = *Source++;
1909 *Dest = color;
1910 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1911 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1912 *Dest |= (1 << 15);
1914 else {
1915 *Dest &= ~(1 << 15);
1917 Dest++;
1921 break;
1923 case CONVERT_CK_RGB24:
1925 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1926 unsigned int x, y;
1927 for (y = 0; y < height; y++)
1929 source = src + pitch * y;
1930 dest = dst + outpitch * y;
1931 for (x = 0; x < width; x++) {
1932 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1933 DWORD dstcolor = color << 8;
1934 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1935 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1936 dstcolor |= 0xff;
1938 *(DWORD*)dest = dstcolor;
1939 source += 3;
1940 dest += 4;
1944 break;
1946 case CONVERT_RGB32_888:
1948 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1949 unsigned int x, y;
1950 for (y = 0; y < height; y++)
1952 source = src + pitch * y;
1953 dest = dst + outpitch * y;
1954 for (x = 0; x < width; x++) {
1955 DWORD color = 0xffffff & *(const DWORD*)source;
1956 DWORD dstcolor = color << 8;
1957 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1958 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1959 dstcolor |= 0xff;
1961 *(DWORD*)dest = dstcolor;
1962 source += 4;
1963 dest += 4;
1967 break;
1969 case CONVERT_V8U8:
1971 unsigned int x, y;
1972 const short *Source;
1973 unsigned char *Dest;
1974 for(y = 0; y < height; y++) {
1975 Source = (const short *)(src + y * pitch);
1976 Dest = dst + y * outpitch;
1977 for (x = 0; x < width; x++ ) {
1978 long color = (*Source++);
1979 /* B */ Dest[0] = 0xff;
1980 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1981 /* R */ Dest[2] = (color) + 128; /* U */
1982 Dest += 3;
1985 break;
1988 case CONVERT_V16U16:
1990 unsigned int x, y;
1991 const DWORD *Source;
1992 unsigned short *Dest;
1993 for(y = 0; y < height; y++) {
1994 Source = (const DWORD *)(src + y * pitch);
1995 Dest = (unsigned short *) (dst + y * outpitch);
1996 for (x = 0; x < width; x++ ) {
1997 DWORD color = (*Source++);
1998 /* B */ Dest[0] = 0xffff;
1999 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2000 /* R */ Dest[2] = (color ) + 32768; /* U */
2001 Dest += 3;
2004 break;
2007 case CONVERT_Q8W8V8U8:
2009 unsigned int x, y;
2010 const DWORD *Source;
2011 unsigned char *Dest;
2012 for(y = 0; y < height; y++) {
2013 Source = (const DWORD *)(src + y * pitch);
2014 Dest = dst + y * outpitch;
2015 for (x = 0; x < width; x++ ) {
2016 long color = (*Source++);
2017 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2018 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2019 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2020 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2021 Dest += 4;
2024 break;
2027 case CONVERT_L6V5U5:
2029 unsigned int x, y;
2030 const WORD *Source;
2031 unsigned char *Dest;
2033 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2034 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2035 * fixed function and shaders without further conversion once the surface is
2036 * loaded
2038 for(y = 0; y < height; y++) {
2039 Source = (const WORD *)(src + y * pitch);
2040 Dest = dst + y * outpitch;
2041 for (x = 0; x < width; x++ ) {
2042 short color = (*Source++);
2043 unsigned char l = ((color >> 10) & 0xfc);
2044 char v = ((color >> 5) & 0x3e);
2045 char u = ((color ) & 0x1f);
2047 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2048 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2049 * shift. GL reads a signed value and converts it into an unsigned value.
2051 /* M */ Dest[2] = l << 1;
2053 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2054 * from 5 bit values to 8 bit values.
2056 /* V */ Dest[1] = v << 3;
2057 /* U */ Dest[0] = u << 3;
2058 Dest += 3;
2061 } else {
2062 for(y = 0; y < height; y++) {
2063 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2064 Source = (const WORD *)(src + y * pitch);
2065 for (x = 0; x < width; x++ ) {
2066 short color = (*Source++);
2067 unsigned char l = ((color >> 10) & 0xfc);
2068 short v = ((color >> 5) & 0x3e);
2069 short u = ((color ) & 0x1f);
2070 short v_conv = v + 16;
2071 short u_conv = u + 16;
2073 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2074 Dest_s += 1;
2078 break;
2081 case CONVERT_X8L8V8U8:
2083 unsigned int x, y;
2084 const DWORD *Source;
2085 unsigned char *Dest;
2087 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2088 /* This implementation works with the fixed function pipeline and shaders
2089 * without further modification after converting the surface.
2091 for(y = 0; y < height; y++) {
2092 Source = (const DWORD *)(src + y * pitch);
2093 Dest = dst + y * outpitch;
2094 for (x = 0; x < width; x++ ) {
2095 long color = (*Source++);
2096 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2097 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2098 /* U */ Dest[0] = (color & 0xff); /* U */
2099 /* I */ Dest[3] = 255; /* X */
2100 Dest += 4;
2103 } else {
2104 /* Doesn't work correctly with the fixed function pipeline, but can work in
2105 * shaders if the shader is adjusted. (There's no use for this format in gl's
2106 * standard fixed function pipeline anyway).
2108 for(y = 0; y < height; y++) {
2109 Source = (const DWORD *)(src + y * pitch);
2110 Dest = dst + y * outpitch;
2111 for (x = 0; x < width; x++ ) {
2112 long color = (*Source++);
2113 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2114 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2115 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2116 Dest += 4;
2120 break;
2123 case CONVERT_A4L4:
2125 unsigned int x, y;
2126 const unsigned char *Source;
2127 unsigned char *Dest;
2128 for(y = 0; y < height; y++) {
2129 Source = src + y * pitch;
2130 Dest = dst + y * outpitch;
2131 for (x = 0; x < width; x++ ) {
2132 unsigned char color = (*Source++);
2133 /* A */ Dest[1] = (color & 0xf0) << 0;
2134 /* L */ Dest[0] = (color & 0x0f) << 4;
2135 Dest += 2;
2138 break;
2141 case CONVERT_G16R16:
2142 case CONVERT_R16G16F:
2144 unsigned int x, y;
2145 const WORD *Source;
2146 WORD *Dest;
2148 for(y = 0; y < height; y++) {
2149 Source = (const WORD *)(src + y * pitch);
2150 Dest = (WORD *) (dst + y * outpitch);
2151 for (x = 0; x < width; x++ ) {
2152 WORD green = (*Source++);
2153 WORD red = (*Source++);
2154 Dest[0] = green;
2155 Dest[1] = red;
2156 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2157 * shader overwrites it anyway
2159 Dest[2] = 0xffff;
2160 Dest += 3;
2163 break;
2166 case CONVERT_R32G32F:
2168 unsigned int x, y;
2169 const float *Source;
2170 float *Dest;
2171 for(y = 0; y < height; y++) {
2172 Source = (const float *)(src + y * pitch);
2173 Dest = (float *) (dst + y * outpitch);
2174 for (x = 0; x < width; x++ ) {
2175 float green = (*Source++);
2176 float red = (*Source++);
2177 Dest[0] = green;
2178 Dest[1] = red;
2179 Dest[2] = 1.0;
2180 Dest += 3;
2183 break;
2186 default:
2187 ERR("Unsupported conversation type %d\n", convert);
2189 return WINED3D_OK;
2192 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2193 IWineD3DPaletteImpl* pal = This->palette;
2194 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2195 BOOL index_in_alpha = FALSE;
2196 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2197 unsigned int i;
2199 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2200 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2201 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2202 * duplicate entries. Store the color key in the unused alpha component to speed the
2203 * download up and to make conversion unneeded. */
2204 index_in_alpha = primary_render_target_is_p8(device);
2206 if (pal == NULL) {
2207 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2208 if(dxVersion <= 7) {
2209 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2210 if(index_in_alpha) {
2211 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2212 there's no palette at this time. */
2213 for (i = 0; i < 256; i++) table[i][3] = i;
2215 } else {
2216 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2217 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2218 capability flag is present (wine does advertise this capability) */
2219 for (i = 0; i < 256; i++) {
2220 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2221 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2222 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2223 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2226 } else {
2227 TRACE("Using surface palette %p\n", pal);
2228 /* Get the surface's palette */
2229 for (i = 0; i < 256; i++) {
2230 table[i][0] = pal->palents[i].peRed;
2231 table[i][1] = pal->palents[i].peGreen;
2232 table[i][2] = pal->palents[i].peBlue;
2234 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2235 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2236 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2237 of pixels that should be masked away is set to 0. */
2238 if(index_in_alpha) {
2239 table[i][3] = i;
2240 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2241 table[i][3] = 0x00;
2242 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2243 table[i][3] = pal->palents[i].peFlags;
2244 } else {
2245 table[i][3] = 0xFF;
2251 /* This function is used in case of 8bit paletted textures to upload the palette.
2252 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2253 extensions like ATI_fragment_shaders is possible.
2255 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2256 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2257 BYTE table[256][4];
2258 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2260 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2262 /* Try to use the paletted texture extension */
2263 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2265 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2266 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2268 else
2270 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2271 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2272 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2274 /* Create the fragment program if we don't have it */
2275 if(!device->paletteConversionShader)
2277 const char *fragment_palette_conversion =
2278 "!!ARBfp1.0\n"
2279 "TEMP index;\n"
2280 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2281 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2282 /* The alpha-component contains the palette index */
2283 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2284 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2285 "MAD index.a, index.a, constants.x, constants.y;\n"
2286 /* Use the alpha-component as an index in the palette to get the final color */
2287 "TEX result.color, index.a, texture[1], 1D;\n"
2288 "END";
2290 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2291 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2292 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2293 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2294 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2297 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2298 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2300 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2301 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2303 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2304 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2305 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2306 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2308 /* Switch back to unit 0 in which the 2D texture will be stored. */
2309 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2311 /* Rebind the texture because it isn't bound anymore */
2312 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2316 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2317 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2319 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2320 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2322 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2323 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2325 return FALSE;
2328 if(This->palette9) {
2329 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2330 return FALSE;
2332 } else {
2333 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2335 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2336 return TRUE;
2339 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2340 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2341 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2343 if (!(This->Flags & flag)) {
2344 TRACE("Reloading because surface is dirty\n");
2345 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2346 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2347 /* Reload: vice versa OR */
2348 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2349 /* Also reload: Color key is active AND the color key has changed */
2350 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2351 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2352 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2353 TRACE("Reloading because of color keying\n");
2354 /* To perform the color key conversion we need a sysmem copy of
2355 * the surface. Make sure we have it
2358 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2359 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2360 /* TODO: This is not necessarily needed with hw palettized texture support */
2361 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2362 } else {
2363 TRACE("surface is already in texture\n");
2364 return WINED3D_OK;
2367 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2368 * These resources are not bound by device size or format restrictions. Because of this,
2369 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2370 * However, these resources can always be created, locked, and copied.
2372 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2374 FIXME("(%p) Operation not supported for scratch textures\n",This);
2375 return WINED3DERR_INVALIDCALL;
2378 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2380 #if 0
2382 static unsigned int gen = 0;
2383 char buffer[4096];
2384 ++gen;
2385 if ((gen % 10) == 0) {
2386 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2387 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2390 * debugging crash code
2391 if (gen == 250) {
2392 void** test = NULL;
2393 *test = 0;
2397 #endif
2399 if (!(This->Flags & SFLAG_DONOTFREE)) {
2400 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2401 This->resource.allocatedMemory = NULL;
2402 This->resource.heapMemory = NULL;
2403 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2406 return WINED3D_OK;
2409 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2410 /* TODO: check for locks */
2411 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2412 IWineD3DBaseTexture *baseTexture = NULL;
2413 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2415 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2416 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2417 TRACE("Passing to container\n");
2418 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2419 IWineD3DBaseTexture_Release(baseTexture);
2420 } else {
2421 GLuint *name;
2422 TRACE("(%p) : Binding surface\n", This);
2424 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2425 if(!device->isInDraw) {
2426 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2429 ENTER_GL();
2431 if (!This->glDescription.level) {
2432 if (!*name) {
2433 glGenTextures(1, name);
2434 checkGLcall("glGenTextures");
2435 TRACE("Surface %p given name %d\n", This, *name);
2437 glBindTexture(This->glDescription.target, *name);
2438 checkGLcall("glBindTexture");
2439 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2440 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2441 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2442 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2443 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2444 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2445 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2446 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2447 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2448 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2450 /* This is where we should be reducing the amount of GLMemoryUsed */
2451 } else if (*name) {
2452 /* Mipmap surfaces should have a base texture container */
2453 ERR("Mipmap surface has a glTexture bound to it!\n");
2456 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2457 checkGLcall("glBindTexture");
2459 LEAVE_GL();
2461 return;
2464 #include <errno.h>
2465 #include <stdio.h>
2466 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2468 FILE* f = NULL;
2469 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2470 char *allocatedMemory;
2471 const char *textureRow;
2472 IWineD3DSwapChain *swapChain = NULL;
2473 int width, height, i, y;
2474 GLuint tmpTexture = 0;
2475 DWORD color;
2476 /*FIXME:
2477 Textures may not be stored in ->allocatedgMemory and a GlTexture
2478 so we should lock the surface before saving a snapshot, or at least check that
2480 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2481 by calling GetTexImage and in compressed form by calling
2482 GetCompressedTexImageARB. Queried compressed images can be saved and
2483 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2484 texture images do not need to be processed by the GL and should
2485 significantly improve texture loading performance relative to uncompressed
2486 images. */
2488 /* Setup the width and height to be the internal texture width and height. */
2489 width = This->pow2Width;
2490 height = This->pow2Height;
2491 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2492 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2494 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2495 /* if were not a real texture then read the back buffer into a real texture */
2496 /* we don't want to interfere with the back buffer so read the data into a temporary
2497 * texture and then save the data out of the temporary texture
2499 GLint prevRead;
2500 ENTER_GL();
2501 TRACE("(%p) Reading render target into texture\n", This);
2503 glGenTextures(1, &tmpTexture);
2504 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2506 glTexImage2D(GL_TEXTURE_2D,
2508 GL_RGBA,
2509 width,
2510 height,
2511 0/*border*/,
2512 GL_RGBA,
2513 GL_UNSIGNED_INT_8_8_8_8_REV,
2514 NULL);
2516 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2517 checkGLcall("glGetIntegerv");
2518 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2519 checkGLcall("glReadBuffer");
2520 glCopyTexImage2D(GL_TEXTURE_2D,
2522 GL_RGBA,
2525 width,
2526 height,
2529 checkGLcall("glCopyTexImage2D");
2530 glReadBuffer(prevRead);
2531 LEAVE_GL();
2533 } else { /* bind the real texture, and make sure it up to date */
2534 surface_internal_preload(iface, SRGB_RGB);
2535 surface_bind_and_dirtify(This, FALSE);
2537 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2538 ENTER_GL();
2539 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2540 glGetTexImage(GL_TEXTURE_2D,
2541 This->glDescription.level,
2542 GL_RGBA,
2543 GL_UNSIGNED_INT_8_8_8_8_REV,
2544 allocatedMemory);
2545 checkGLcall("glTexImage2D");
2546 if (tmpTexture) {
2547 glBindTexture(GL_TEXTURE_2D, 0);
2548 glDeleteTextures(1, &tmpTexture);
2550 LEAVE_GL();
2552 f = fopen(filename, "w+");
2553 if (NULL == f) {
2554 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2555 return WINED3DERR_INVALIDCALL;
2557 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2558 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2559 /* TGA header */
2560 fputc(0,f);
2561 fputc(0,f);
2562 fputc(2,f);
2563 fputc(0,f);
2564 fputc(0,f);
2565 fputc(0,f);
2566 fputc(0,f);
2567 fputc(0,f);
2568 fputc(0,f);
2569 fputc(0,f);
2570 fputc(0,f);
2571 fputc(0,f);
2572 /* short width*/
2573 fwrite(&width,2,1,f);
2574 /* short height */
2575 fwrite(&height,2,1,f);
2576 /* format rgba */
2577 fputc(0x20,f);
2578 fputc(0x28,f);
2579 /* raw data */
2580 /* 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 */
2581 if(swapChain)
2582 textureRow = allocatedMemory + (width * (height - 1) *4);
2583 else
2584 textureRow = allocatedMemory;
2585 for (y = 0 ; y < height; y++) {
2586 for (i = 0; i < width; i++) {
2587 color = *((const DWORD*)textureRow);
2588 fputc((color >> 16) & 0xFF, f); /* B */
2589 fputc((color >> 8) & 0xFF, f); /* G */
2590 fputc((color >> 0) & 0xFF, f); /* R */
2591 fputc((color >> 24) & 0xFF, f); /* A */
2592 textureRow += 4;
2594 /* take two rows of the pointer to the texture memory */
2595 if(swapChain)
2596 (textureRow-= width << 3);
2599 TRACE("Closing file\n");
2600 fclose(f);
2602 if(swapChain) {
2603 IWineD3DSwapChain_Release(swapChain);
2605 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2606 return WINED3D_OK;
2609 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2610 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2611 HRESULT hr;
2613 TRACE("(%p) : Calling base function first\n", This);
2614 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2615 if(SUCCEEDED(hr)) {
2616 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2617 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2618 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2620 return hr;
2623 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2624 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2626 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2627 WARN("Surface is locked or the HDC is in use\n");
2628 return WINED3DERR_INVALIDCALL;
2631 if(Mem && Mem != This->resource.allocatedMemory) {
2632 void *release = NULL;
2634 /* Do I have to copy the old surface content? */
2635 if(This->Flags & SFLAG_DIBSECTION) {
2636 /* Release the DC. No need to hold the critical section for the update
2637 * Thread because this thread runs only on front buffers, but this method
2638 * fails for render targets in the check above.
2640 SelectObject(This->hDC, This->dib.holdbitmap);
2641 DeleteDC(This->hDC);
2642 /* Release the DIB section */
2643 DeleteObject(This->dib.DIBsection);
2644 This->dib.bitmap_data = NULL;
2645 This->resource.allocatedMemory = NULL;
2646 This->hDC = NULL;
2647 This->Flags &= ~SFLAG_DIBSECTION;
2648 } else if(!(This->Flags & SFLAG_USERPTR)) {
2649 release = This->resource.heapMemory;
2650 This->resource.heapMemory = NULL;
2652 This->resource.allocatedMemory = Mem;
2653 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2655 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2656 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2658 /* For client textures opengl has to be notified */
2659 if(This->Flags & SFLAG_CLIENT) {
2660 DWORD oldFlags = This->Flags;
2661 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2662 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2663 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2664 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2667 /* Now free the old memory if any */
2668 HeapFree(GetProcessHeap(), 0, release);
2669 } else if(This->Flags & SFLAG_USERPTR) {
2670 /* LockRect and GetDC will re-create the dib section and allocated memory */
2671 This->resource.allocatedMemory = NULL;
2672 /* HeapMemory should be NULL already */
2673 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2674 This->Flags &= ~SFLAG_USERPTR;
2676 if(This->Flags & SFLAG_CLIENT) {
2677 DWORD oldFlags = This->Flags;
2678 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2679 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2680 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2681 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2684 return WINED3D_OK;
2687 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2689 /* Flip the surface contents */
2690 /* Flip the DC */
2692 HDC tmp;
2693 tmp = front->hDC;
2694 front->hDC = back->hDC;
2695 back->hDC = tmp;
2698 /* Flip the DIBsection */
2700 HBITMAP tmp;
2701 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2702 tmp = front->dib.DIBsection;
2703 front->dib.DIBsection = back->dib.DIBsection;
2704 back->dib.DIBsection = tmp;
2706 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2707 else front->Flags &= ~SFLAG_DIBSECTION;
2708 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2709 else back->Flags &= ~SFLAG_DIBSECTION;
2712 /* Flip the surface data */
2714 void* tmp;
2716 tmp = front->dib.bitmap_data;
2717 front->dib.bitmap_data = back->dib.bitmap_data;
2718 back->dib.bitmap_data = tmp;
2720 tmp = front->resource.allocatedMemory;
2721 front->resource.allocatedMemory = back->resource.allocatedMemory;
2722 back->resource.allocatedMemory = tmp;
2724 tmp = front->resource.heapMemory;
2725 front->resource.heapMemory = back->resource.heapMemory;
2726 back->resource.heapMemory = tmp;
2729 /* Flip the PBO */
2731 GLuint tmp_pbo = front->pbo;
2732 front->pbo = back->pbo;
2733 back->pbo = tmp_pbo;
2736 /* client_memory should not be different, but just in case */
2738 BOOL tmp;
2739 tmp = front->dib.client_memory;
2740 front->dib.client_memory = back->dib.client_memory;
2741 back->dib.client_memory = tmp;
2744 /* Flip the opengl texture */
2746 glDescriptor tmp_desc = back->glDescription;
2747 back->glDescription = front->glDescription;
2748 front->glDescription = tmp_desc;
2752 DWORD tmp_flags = back->Flags;
2753 back->Flags = front->Flags;
2754 front->Flags = tmp_flags;
2758 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2759 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2760 IWineD3DSwapChainImpl *swapchain = NULL;
2761 HRESULT hr;
2762 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2764 /* Flipping is only supported on RenderTargets and overlays*/
2765 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2766 WARN("Tried to flip a non-render target, non-overlay surface\n");
2767 return WINEDDERR_NOTFLIPPABLE;
2770 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2771 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2773 /* Update the overlay if it is visible */
2774 if(This->overlay_dest) {
2775 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2776 } else {
2777 return WINED3D_OK;
2781 if(override) {
2782 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2783 * FIXME("(%p) Target override is not supported by now\n", This);
2784 * Additionally, it isn't really possible to support triple-buffering
2785 * properly on opengl at all
2789 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2790 if(!swapchain) {
2791 ERR("Flipped surface is not on a swapchain\n");
2792 return WINEDDERR_NOTFLIPPABLE;
2795 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2796 * and only d3d8 and d3d9 apps specify the presentation interval
2798 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2799 /* Most common case first to avoid wasting time on all the other cases */
2800 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2801 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2802 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2803 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2804 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2805 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2806 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2807 } else {
2808 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2811 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2812 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2813 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2814 return hr;
2817 /* Does a direct frame buffer -> texture copy. Stretching is done
2818 * with single pixel copy calls
2820 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2821 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2822 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2824 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2825 float xrel, yrel;
2826 UINT row;
2827 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2830 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2831 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2832 ENTER_GL();
2834 /* Bind the target texture */
2835 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2836 checkGLcall("glBindTexture");
2837 if(!swapchain) {
2838 TRACE("Reading from an offscreen target\n");
2839 upsidedown = !upsidedown;
2840 glReadBuffer(myDevice->offscreenBuffer);
2841 } else {
2842 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2843 glReadBuffer(buffer);
2845 checkGLcall("glReadBuffer");
2847 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2848 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2850 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2851 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2853 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2854 ERR("Texture filtering not supported in direct blit\n");
2856 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2857 ERR("Texture filtering not supported in direct blit\n");
2860 if(upsidedown &&
2861 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2862 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2863 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2865 glCopyTexSubImage2D(This->glDescription.target,
2866 This->glDescription.level,
2867 drect->x1, drect->y1, /* xoffset, yoffset */
2868 srect->x1, Src->currentDesc.Height - srect->y2,
2869 drect->x2 - drect->x1, drect->y2 - drect->y1);
2870 } else {
2871 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2872 /* I have to process this row by row to swap the image,
2873 * otherwise it would be upside down, so stretching in y direction
2874 * doesn't cost extra time
2876 * However, stretching in x direction can be avoided if not necessary
2878 for(row = drect->y1; row < drect->y2; row++) {
2879 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2880 /* Well, that stuff works, but it's very slow.
2881 * find a better way instead
2883 UINT col;
2885 for(col = drect->x1; col < drect->x2; col++) {
2886 glCopyTexSubImage2D(This->glDescription.target,
2887 This->glDescription.level,
2888 drect->x1 + col, row, /* xoffset, yoffset */
2889 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2890 1, 1);
2892 } else {
2893 glCopyTexSubImage2D(This->glDescription.target,
2894 This->glDescription.level,
2895 drect->x1, row, /* xoffset, yoffset */
2896 srect->x1, yoffset - (int) (row * yrel),
2897 drect->x2-drect->x1, 1);
2901 checkGLcall("glCopyTexSubImage2D");
2903 LEAVE_GL();
2906 /* Uses the hardware to stretch and flip the image */
2907 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2908 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2909 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2911 GLuint src, backup = 0;
2912 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2913 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2914 float left, right, top, bottom; /* Texture coordinates */
2915 UINT fbwidth = Src->currentDesc.Width;
2916 UINT fbheight = Src->currentDesc.Height;
2917 GLenum drawBuffer = GL_BACK;
2918 GLenum texture_target;
2919 BOOL noBackBufferBackup;
2921 TRACE("Using hwstretch blit\n");
2922 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2923 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2924 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2926 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2927 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2928 /* Get it a description */
2929 surface_internal_preload(SrcSurface, SRGB_RGB);
2931 ENTER_GL();
2933 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2934 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2936 if(myDevice->activeContext->aux_buffers >= 2) {
2937 /* Got more than one aux buffer? Use the 2nd aux buffer */
2938 drawBuffer = GL_AUX1;
2939 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2940 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2941 drawBuffer = GL_AUX0;
2944 if(noBackBufferBackup) {
2945 glGenTextures(1, &backup);
2946 checkGLcall("glGenTextures\n");
2947 glBindTexture(GL_TEXTURE_2D, backup);
2948 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2949 texture_target = GL_TEXTURE_2D;
2950 } else {
2951 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2952 * we are reading from the back buffer, the backup can be used as source texture
2954 texture_target = Src->glDescription.target;
2955 glBindTexture(texture_target, Src->glDescription.textureName);
2956 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2957 glEnable(texture_target);
2958 checkGLcall("glEnable(texture_target)");
2960 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2961 Src->Flags &= ~SFLAG_INTEXTURE;
2964 if(swapchain) {
2965 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2966 } else {
2967 TRACE("Reading from an offscreen target\n");
2968 upsidedown = !upsidedown;
2969 glReadBuffer(myDevice->offscreenBuffer);
2972 /* TODO: Only back up the part that will be overwritten */
2973 glCopyTexSubImage2D(texture_target, 0,
2974 0, 0 /* read offsets */,
2975 0, 0,
2976 fbwidth,
2977 fbheight);
2979 checkGLcall("glCopyTexSubImage2D");
2981 /* No issue with overriding these - the sampler is dirty due to blit usage */
2982 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2983 magLookup[Filter - WINED3DTEXF_NONE]);
2984 checkGLcall("glTexParameteri");
2985 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2986 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
2987 checkGLcall("glTexParameteri");
2989 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2990 src = backup ? backup : Src->glDescription.textureName;
2991 } else {
2992 glReadBuffer(GL_FRONT);
2993 checkGLcall("glReadBuffer(GL_FRONT)");
2995 glGenTextures(1, &src);
2996 checkGLcall("glGenTextures(1, &src)");
2997 glBindTexture(GL_TEXTURE_2D, src);
2998 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3000 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3001 * out for power of 2 sizes
3003 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3004 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3005 checkGLcall("glTexImage2D");
3006 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3007 0, 0 /* read offsets */,
3008 0, 0,
3009 fbwidth,
3010 fbheight);
3012 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3013 checkGLcall("glTexParameteri");
3014 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3015 checkGLcall("glTexParameteri");
3017 glReadBuffer(GL_BACK);
3018 checkGLcall("glReadBuffer(GL_BACK)");
3020 if(texture_target != GL_TEXTURE_2D) {
3021 glDisable(texture_target);
3022 glEnable(GL_TEXTURE_2D);
3023 texture_target = GL_TEXTURE_2D;
3026 checkGLcall("glEnd and previous");
3028 left = srect->x1;
3029 right = srect->x2;
3031 if(upsidedown) {
3032 top = Src->currentDesc.Height - srect->y1;
3033 bottom = Src->currentDesc.Height - srect->y2;
3034 } else {
3035 top = Src->currentDesc.Height - srect->y2;
3036 bottom = Src->currentDesc.Height - srect->y1;
3039 if(Src->Flags & SFLAG_NORMCOORD) {
3040 left /= Src->pow2Width;
3041 right /= Src->pow2Width;
3042 top /= Src->pow2Height;
3043 bottom /= Src->pow2Height;
3046 /* draw the source texture stretched and upside down. The correct surface is bound already */
3047 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3048 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3050 glDrawBuffer(drawBuffer);
3051 glReadBuffer(drawBuffer);
3053 glBegin(GL_QUADS);
3054 /* bottom left */
3055 glTexCoord2f(left, bottom);
3056 glVertex2i(0, fbheight);
3058 /* top left */
3059 glTexCoord2f(left, top);
3060 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3062 /* top right */
3063 glTexCoord2f(right, top);
3064 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3066 /* bottom right */
3067 glTexCoord2f(right, bottom);
3068 glVertex2i(drect->x2 - drect->x1, fbheight);
3069 glEnd();
3070 checkGLcall("glEnd and previous");
3072 if(texture_target != This->glDescription.target) {
3073 glDisable(texture_target);
3074 glEnable(This->glDescription.target);
3075 texture_target = This->glDescription.target;
3078 /* Now read the stretched and upside down image into the destination texture */
3079 glBindTexture(texture_target, This->glDescription.textureName);
3080 checkGLcall("glBindTexture");
3081 glCopyTexSubImage2D(texture_target,
3083 drect->x1, drect->y1, /* xoffset, yoffset */
3084 0, 0, /* We blitted the image to the origin */
3085 drect->x2 - drect->x1, drect->y2 - drect->y1);
3086 checkGLcall("glCopyTexSubImage2D");
3088 if(drawBuffer == GL_BACK) {
3089 /* Write the back buffer backup back */
3090 if(backup) {
3091 if(texture_target != GL_TEXTURE_2D) {
3092 glDisable(texture_target);
3093 glEnable(GL_TEXTURE_2D);
3094 texture_target = GL_TEXTURE_2D;
3096 glBindTexture(GL_TEXTURE_2D, backup);
3097 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3098 } else {
3099 if(texture_target != Src->glDescription.target) {
3100 glDisable(texture_target);
3101 glEnable(Src->glDescription.target);
3102 texture_target = Src->glDescription.target;
3104 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3105 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3108 glBegin(GL_QUADS);
3109 /* top left */
3110 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3111 glVertex2i(0, 0);
3113 /* bottom left */
3114 glTexCoord2f(0.0, 0.0);
3115 glVertex2i(0, fbheight);
3117 /* bottom right */
3118 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3119 glVertex2i(fbwidth, Src->currentDesc.Height);
3121 /* top right */
3122 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3123 glVertex2i(fbwidth, 0);
3124 glEnd();
3125 } else {
3126 /* Restore the old draw buffer */
3127 glDrawBuffer(GL_BACK);
3129 glDisable(texture_target);
3130 checkGLcall("glDisable(texture_target)");
3132 /* Cleanup */
3133 if(src != Src->glDescription.textureName && src != backup) {
3134 glDeleteTextures(1, &src);
3135 checkGLcall("glDeleteTextures(1, &src)");
3137 if(backup) {
3138 glDeleteTextures(1, &backup);
3139 checkGLcall("glDeleteTextures(1, &backup)");
3142 LEAVE_GL();
3145 /* Not called from the VTable */
3146 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3147 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3148 WINED3DTEXTUREFILTERTYPE Filter)
3150 WINED3DRECT rect;
3151 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3152 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3153 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3155 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3157 /* Get the swapchain. One of the surfaces has to be a primary surface */
3158 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3159 WARN("Destination is in sysmem, rejecting gl blt\n");
3160 return WINED3DERR_INVALIDCALL;
3162 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3163 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3164 if(Src) {
3165 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3166 WARN("Src is in sysmem, rejecting gl blt\n");
3167 return WINED3DERR_INVALIDCALL;
3169 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3170 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3173 /* Early sort out of cases where no render target is used */
3174 if(!dstSwapchain && !srcSwapchain &&
3175 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3176 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3177 return WINED3DERR_INVALIDCALL;
3180 /* No destination color keying supported */
3181 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3182 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3183 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3184 return WINED3DERR_INVALIDCALL;
3187 if (DestRect) {
3188 rect.x1 = DestRect->left;
3189 rect.y1 = DestRect->top;
3190 rect.x2 = DestRect->right;
3191 rect.y2 = DestRect->bottom;
3192 } else {
3193 rect.x1 = 0;
3194 rect.y1 = 0;
3195 rect.x2 = This->currentDesc.Width;
3196 rect.y2 = This->currentDesc.Height;
3199 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3200 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3201 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3202 /* Half-life does a Blt from the back buffer to the front buffer,
3203 * Full surface size, no flags... Use present instead
3205 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3208 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3209 while(1)
3211 RECT mySrcRect;
3212 TRACE("Looking if a Present can be done...\n");
3213 /* Source Rectangle must be full surface */
3214 if( SrcRect ) {
3215 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3216 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3217 TRACE("No, Source rectangle doesn't match\n");
3218 break;
3221 mySrcRect.left = 0;
3222 mySrcRect.top = 0;
3223 mySrcRect.right = Src->currentDesc.Width;
3224 mySrcRect.bottom = Src->currentDesc.Height;
3226 /* No stretching may occur */
3227 if(mySrcRect.right != rect.x2 - rect.x1 ||
3228 mySrcRect.bottom != rect.y2 - rect.y1) {
3229 TRACE("No, stretching is done\n");
3230 break;
3233 /* Destination must be full surface or match the clipping rectangle */
3234 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3236 RECT cliprect;
3237 POINT pos[2];
3238 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3239 pos[0].x = rect.x1;
3240 pos[0].y = rect.y1;
3241 pos[1].x = rect.x2;
3242 pos[1].y = rect.y2;
3243 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3244 pos, 2);
3246 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3247 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3249 TRACE("No, dest rectangle doesn't match(clipper)\n");
3250 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3251 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3252 break;
3255 else
3257 if(rect.x1 != 0 || rect.y1 != 0 ||
3258 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3259 TRACE("No, dest rectangle doesn't match(surface size)\n");
3260 break;
3264 TRACE("Yes\n");
3266 /* These flags are unimportant for the flag check, remove them */
3267 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3268 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3270 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3271 * take very long, while a flip is fast.
3272 * This applies to Half-Life, which does such Blts every time it finished
3273 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3274 * menu. This is also used by all apps when they do windowed rendering
3276 * The problem is that flipping is not really the same as copying. After a
3277 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3278 * untouched. Therefore it's necessary to override the swap effect
3279 * and to set it back after the flip.
3281 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3282 * testcases.
3285 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3286 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3288 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3289 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3291 dstSwapchain->presentParms.SwapEffect = orig_swap;
3293 return WINED3D_OK;
3295 break;
3298 TRACE("Unsupported blit between buffers on the same swapchain\n");
3299 return WINED3DERR_INVALIDCALL;
3300 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3301 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3302 return WINED3DERR_INVALIDCALL;
3303 } else if(dstSwapchain && srcSwapchain) {
3304 FIXME("Implement hardware blit between two different swapchains\n");
3305 return WINED3DERR_INVALIDCALL;
3306 } else if(dstSwapchain) {
3307 if(SrcSurface == myDevice->render_targets[0]) {
3308 TRACE("Blit from active render target to a swapchain\n");
3309 /* Handled with regular texture -> swapchain blit */
3311 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3312 FIXME("Implement blit from a swapchain to the active render target\n");
3313 return WINED3DERR_INVALIDCALL;
3316 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3317 /* Blit from render target to texture */
3318 WINED3DRECT srect;
3319 BOOL upsideDown, stretchx;
3320 BOOL paletteOverride = FALSE;
3322 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3323 TRACE("Color keying not supported by frame buffer to texture blit\n");
3324 return WINED3DERR_INVALIDCALL;
3325 /* Destination color key is checked above */
3328 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3329 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3331 if(SrcRect) {
3332 if(SrcRect->top < SrcRect->bottom) {
3333 srect.y1 = SrcRect->top;
3334 srect.y2 = SrcRect->bottom;
3335 upsideDown = FALSE;
3336 } else {
3337 srect.y1 = SrcRect->bottom;
3338 srect.y2 = SrcRect->top;
3339 upsideDown = TRUE;
3341 srect.x1 = SrcRect->left;
3342 srect.x2 = SrcRect->right;
3343 } else {
3344 srect.x1 = 0;
3345 srect.y1 = 0;
3346 srect.x2 = Src->currentDesc.Width;
3347 srect.y2 = Src->currentDesc.Height;
3348 upsideDown = FALSE;
3350 if(rect.x1 > rect.x2) {
3351 UINT tmp = rect.x2;
3352 rect.x2 = rect.x1;
3353 rect.x1 = tmp;
3354 upsideDown = !upsideDown;
3357 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3358 stretchx = TRUE;
3359 } else {
3360 stretchx = FALSE;
3363 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3364 * In this case grab the palette from the render target. */
3365 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3367 paletteOverride = TRUE;
3368 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3369 This->palette = Src->palette;
3372 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3373 * flip the image nor scale it.
3375 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3376 * -> If the app wants a image width an unscaled width, copy it line per line
3377 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3378 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3379 * back buffer. This is slower than reading line per line, thus not used for flipping
3380 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3381 * pixel by pixel
3383 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3384 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3385 * backends.
3387 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3388 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3389 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3390 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3391 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3392 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3393 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3394 } else {
3395 TRACE("Using hardware stretching to flip / stretch the texture\n");
3396 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3399 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3400 if(paletteOverride)
3401 This->palette = NULL;
3403 if(!(This->Flags & SFLAG_DONOTFREE)) {
3404 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3405 This->resource.allocatedMemory = NULL;
3406 This->resource.heapMemory = NULL;
3407 } else {
3408 This->Flags &= ~SFLAG_INSYSMEM;
3410 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3411 * path is never entered
3413 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3415 return WINED3D_OK;
3416 } else if(Src) {
3417 /* Blit from offscreen surface to render target */
3418 float glTexCoord[4];
3419 DWORD oldCKeyFlags = Src->CKeyFlags;
3420 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3421 RECT SourceRectangle;
3422 BOOL paletteOverride = FALSE;
3424 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3426 if(SrcRect) {
3427 SourceRectangle.left = SrcRect->left;
3428 SourceRectangle.right = SrcRect->right;
3429 SourceRectangle.top = SrcRect->top;
3430 SourceRectangle.bottom = SrcRect->bottom;
3431 } else {
3432 SourceRectangle.left = 0;
3433 SourceRectangle.right = Src->currentDesc.Width;
3434 SourceRectangle.top = 0;
3435 SourceRectangle.bottom = Src->currentDesc.Height;
3438 /* When blitting from an offscreen surface to a rendertarget, the source
3439 * surface is not required to have a palette. Our rendering / conversion
3440 * code further down the road retrieves the palette from the surface, so
3441 * it must have a palette set. */
3442 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3444 paletteOverride = TRUE;
3445 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3446 Src->palette = This->palette;
3449 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3450 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3451 TRACE("Using stretch_rect_fbo\n");
3452 /* The source is always a texture, but never the currently active render target, and the texture
3453 * contents are never upside down
3455 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3456 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3458 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3459 if(paletteOverride)
3460 Src->palette = NULL;
3461 return WINED3D_OK;
3464 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3465 /* Fall back to software */
3466 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3467 SourceRectangle.left, SourceRectangle.top,
3468 SourceRectangle.right, SourceRectangle.bottom);
3469 return WINED3DERR_INVALIDCALL;
3472 /* Color keying: Check if we have to do a color keyed blt,
3473 * and if not check if a color key is activated.
3475 * Just modify the color keying parameters in the surface and restore them afterwards
3476 * The surface keeps track of the color key last used to load the opengl surface.
3477 * PreLoad will catch the change to the flags and color key and reload if necessary.
3479 if(Flags & WINEDDBLT_KEYSRC) {
3480 /* Use color key from surface */
3481 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3482 /* Use color key from DDBltFx */
3483 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3484 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3485 } else {
3486 /* Do not use color key */
3487 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3490 /* Now load the surface */
3491 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3493 /* Activate the destination context, set it up for blitting */
3494 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3496 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3497 * while OpenGL coordinates are window relative.
3498 * Also beware of the origin difference(top left vs bottom left).
3499 * Also beware that the front buffer's surface size is screen width x screen height,
3500 * whereas the real gl drawable size is the size of the window.
3502 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3503 RECT windowsize;
3504 POINT offset = {0,0};
3505 UINT h;
3506 ClientToScreen(dstSwapchain->win_handle, &offset);
3507 GetClientRect(dstSwapchain->win_handle, &windowsize);
3508 h = windowsize.bottom - windowsize.top;
3509 rect.x1 -= offset.x; rect.x2 -=offset.x;
3510 rect.y1 -= offset.y; rect.y2 -=offset.y;
3511 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3514 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3515 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3517 ENTER_GL();
3519 /* Bind the texture */
3520 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3521 checkGLcall("glBindTexture");
3523 /* Filtering for StretchRect */
3524 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3525 magLookup[Filter - WINED3DTEXF_NONE]);
3526 checkGLcall("glTexParameteri");
3527 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3528 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3529 checkGLcall("glTexParameteri");
3530 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3531 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3532 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3533 checkGLcall("glTexEnvi");
3535 /* This is for color keying */
3536 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3537 glEnable(GL_ALPHA_TEST);
3538 checkGLcall("glEnable GL_ALPHA_TEST");
3540 /* When the primary render target uses P8, the alpha component contains the palette index.
3541 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3542 * should be masked away have alpha set to 0. */
3543 if(primary_render_target_is_p8(myDevice))
3544 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3545 else
3546 glAlphaFunc(GL_NOTEQUAL, 0.0);
3547 checkGLcall("glAlphaFunc\n");
3548 } else {
3549 glDisable(GL_ALPHA_TEST);
3550 checkGLcall("glDisable GL_ALPHA_TEST");
3553 /* Draw a textured quad
3555 glBegin(GL_QUADS);
3557 glColor3d(1.0f, 1.0f, 1.0f);
3558 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3559 glVertex3f(rect.x1,
3560 rect.y1,
3561 0.0);
3563 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3564 glVertex3f(rect.x1, rect.y2, 0.0);
3566 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3567 glVertex3f(rect.x2,
3568 rect.y2,
3569 0.0);
3571 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3572 glVertex3f(rect.x2,
3573 rect.y1,
3574 0.0);
3575 glEnd();
3576 checkGLcall("glEnd");
3578 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3579 glDisable(GL_ALPHA_TEST);
3580 checkGLcall("glDisable(GL_ALPHA_TEST)");
3583 glBindTexture(Src->glDescription.target, 0);
3584 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3586 /* Restore the color key parameters */
3587 Src->CKeyFlags = oldCKeyFlags;
3588 Src->SrcBltCKey = oldBltCKey;
3590 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3591 if(paletteOverride)
3592 Src->palette = NULL;
3594 LEAVE_GL();
3596 /* Leave the opengl state valid for blitting */
3597 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3599 /* Flush in case the drawable is used by multiple GL contexts */
3600 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3601 glFlush();
3603 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3604 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3605 * is outdated now
3607 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3609 return WINED3D_OK;
3610 } else {
3611 /* Source-Less Blit to render target */
3612 if (Flags & WINEDDBLT_COLORFILL) {
3613 /* This is easy to handle for the D3D Device... */
3614 DWORD color;
3616 TRACE("Colorfill\n");
3618 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3619 must be true if we are here */
3620 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3621 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3622 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3623 TRACE("Surface is higher back buffer, falling back to software\n");
3624 return WINED3DERR_INVALIDCALL;
3627 /* The color as given in the Blt function is in the format of the frame-buffer...
3628 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3630 if (This->resource.format_desc->format == WINED3DFMT_P8)
3632 DWORD alpha;
3634 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3635 else alpha = 0xFF000000;
3637 if (This->palette) {
3638 color = (alpha |
3639 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3640 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3641 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3642 } else {
3643 color = alpha;
3646 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3648 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3649 color = 0xFFFFFFFF;
3650 } else {
3651 color = ((0xFF000000) |
3652 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3653 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3654 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3657 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3658 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3660 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3662 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3664 color = DDBltFx->u5.dwFillColor;
3666 else {
3667 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3668 return WINED3DERR_INVALIDCALL;
3671 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3672 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3673 1, /* Number of rectangles */
3674 &rect, WINED3DCLEAR_TARGET, color,
3675 0.0 /* Z */,
3676 0 /* Stencil */);
3677 return WINED3D_OK;
3681 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3682 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3683 return WINED3DERR_INVALIDCALL;
3686 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3687 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3689 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3690 float depth;
3692 if (Flags & WINEDDBLT_DEPTHFILL) {
3693 switch(This->resource.format_desc->format)
3695 case WINED3DFMT_D16_UNORM:
3696 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3697 break;
3698 case WINED3DFMT_D15S1:
3699 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3700 break;
3701 case WINED3DFMT_D24S8:
3702 case WINED3DFMT_D24X8:
3703 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3704 break;
3705 case WINED3DFMT_D32:
3706 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3707 break;
3708 default:
3709 depth = 0.0;
3710 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3713 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3714 DestRect == NULL ? 0 : 1,
3715 (const WINED3DRECT *)DestRect,
3716 WINED3DCLEAR_ZBUFFER,
3717 0x00000000,
3718 depth,
3719 0x00000000);
3722 FIXME("(%p): Unsupp depthstencil blit\n", This);
3723 return WINED3DERR_INVALIDCALL;
3726 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3727 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3728 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3729 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3730 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3731 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3732 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3734 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3736 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3737 return WINEDDERR_SURFACEBUSY;
3740 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3741 * except depth blits, which seem to work
3743 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3744 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3745 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3746 return WINED3DERR_INVALIDCALL;
3747 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3748 TRACE("Z Blit override handled the blit\n");
3749 return WINED3D_OK;
3753 /* Special cases for RenderTargets */
3754 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3755 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3756 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3759 /* For the rest call the X11 surface implementation.
3760 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3761 * other Blts are rather rare
3763 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3766 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3767 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3769 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3770 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3771 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3772 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3774 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3776 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3777 return WINEDDERR_SURFACEBUSY;
3780 if(myDevice->inScene &&
3781 (iface == myDevice->stencilBufferTarget ||
3782 (Source && Source == myDevice->stencilBufferTarget))) {
3783 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3784 return WINED3DERR_INVALIDCALL;
3787 /* Special cases for RenderTargets */
3788 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3789 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3791 RECT SrcRect, DstRect;
3792 DWORD Flags=0;
3794 if(rsrc) {
3795 SrcRect.left = rsrc->left;
3796 SrcRect.top= rsrc->top;
3797 SrcRect.bottom = rsrc->bottom;
3798 SrcRect.right = rsrc->right;
3799 } else {
3800 SrcRect.left = 0;
3801 SrcRect.top = 0;
3802 SrcRect.right = srcImpl->currentDesc.Width;
3803 SrcRect.bottom = srcImpl->currentDesc.Height;
3806 DstRect.left = dstx;
3807 DstRect.top=dsty;
3808 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3809 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3811 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3812 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3813 Flags |= WINEDDBLT_KEYSRC;
3814 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3815 Flags |= WINEDDBLT_KEYDEST;
3816 if(trans & WINEDDBLTFAST_WAIT)
3817 Flags |= WINEDDBLT_WAIT;
3818 if(trans & WINEDDBLTFAST_DONOTWAIT)
3819 Flags |= WINEDDBLT_DONOTWAIT;
3821 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3825 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3828 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3830 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3831 RGBQUAD col[256];
3832 IWineD3DPaletteImpl *pal = This->palette;
3833 unsigned int n;
3834 TRACE("(%p)\n", This);
3836 if (!pal) return WINED3D_OK;
3838 if (This->resource.format_desc->format == WINED3DFMT_P8
3839 || This->resource.format_desc->format == WINED3DFMT_A8P8)
3841 int bpp;
3842 GLenum format, internal, type;
3843 CONVERT_TYPES convert;
3845 /* Check if we are using a RTL mode which uses texturing for uploads */
3846 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3848 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3849 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
3851 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3853 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3854 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3856 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3857 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3859 /* Re-upload the palette */
3860 d3dfmt_p8_upload_palette(iface, convert);
3861 } else {
3862 if(!(This->Flags & SFLAG_INSYSMEM)) {
3863 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3864 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3866 TRACE("Dirtifying surface\n");
3867 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3871 if(This->Flags & SFLAG_DIBSECTION) {
3872 TRACE("(%p): Updating the hdc's palette\n", This);
3873 for (n=0; n<256; n++) {
3874 col[n].rgbRed = pal->palents[n].peRed;
3875 col[n].rgbGreen = pal->palents[n].peGreen;
3876 col[n].rgbBlue = pal->palents[n].peBlue;
3877 col[n].rgbReserved = 0;
3879 SetDIBColorTable(This->hDC, 0, 256, col);
3882 /* Propagate the changes to the drawable when we have a palette. */
3883 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3884 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3886 return WINED3D_OK;
3889 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3890 /** Check against the maximum texture sizes supported by the video card **/
3891 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3892 unsigned int pow2Width, pow2Height;
3894 This->glDescription.textureName = 0;
3895 This->glDescription.target = GL_TEXTURE_2D;
3897 /* Non-power2 support */
3898 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3899 pow2Width = This->currentDesc.Width;
3900 pow2Height = This->currentDesc.Height;
3901 } else {
3902 /* Find the nearest pow2 match */
3903 pow2Width = pow2Height = 1;
3904 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3905 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3907 This->pow2Width = pow2Width;
3908 This->pow2Height = pow2Height;
3910 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3911 WINED3DFORMAT Format = This->resource.format_desc->format;
3912 /** TODO: add support for non power two compressed textures **/
3913 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3914 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3915 || Format == WINED3DFMT_ATI2N)
3917 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3918 This, This->currentDesc.Width, This->currentDesc.Height);
3919 return WINED3DERR_NOTAVAILABLE;
3923 if(pow2Width != This->currentDesc.Width ||
3924 pow2Height != This->currentDesc.Height) {
3925 This->Flags |= SFLAG_NONPOW2;
3928 TRACE("%p\n", This);
3929 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3930 /* one of three options
3931 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)
3932 2: Set the texture to the maximum size (bad idea)
3933 3: WARN and return WINED3DERR_NOTAVAILABLE;
3934 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.
3936 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
3937 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
3938 This->Flags |= SFLAG_OVERSIZE;
3940 /* This will be initialized on the first blt */
3941 This->glRect.left = 0;
3942 This->glRect.top = 0;
3943 This->glRect.right = 0;
3944 This->glRect.bottom = 0;
3945 } else {
3946 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3947 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3948 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3949 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3951 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
3952 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
3953 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
3954 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3956 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3957 This->pow2Width = This->currentDesc.Width;
3958 This->pow2Height = This->currentDesc.Height;
3959 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3962 /* No oversize, gl rect is the full texture size */
3963 This->Flags &= ~SFLAG_OVERSIZE;
3964 This->glRect.left = 0;
3965 This->glRect.top = 0;
3966 This->glRect.right = This->pow2Width;
3967 This->glRect.bottom = This->pow2Height;
3970 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3971 switch(wined3d_settings.offscreen_rendering_mode) {
3972 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3973 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3974 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3978 This->Flags |= SFLAG_INSYSMEM;
3980 return WINED3D_OK;
3983 struct depth_blt_info
3985 GLenum binding;
3986 GLenum bind_target;
3987 enum tex_types tex_type;
3988 GLfloat coords[4][3];
3991 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3993 GLfloat (*coords)[3] = info->coords;
3995 switch (target)
3997 default:
3998 FIXME("Unsupported texture target %#x\n", target);
3999 /* Fall back to GL_TEXTURE_2D */
4000 case GL_TEXTURE_2D:
4001 info->binding = GL_TEXTURE_BINDING_2D;
4002 info->bind_target = GL_TEXTURE_2D;
4003 info->tex_type = tex_2d;
4004 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4005 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4006 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4007 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4008 break;
4010 case GL_TEXTURE_RECTANGLE_ARB:
4011 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4012 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4013 info->tex_type = tex_rect;
4014 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4015 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4016 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4017 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4018 break;
4020 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4021 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4022 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4023 info->tex_type = tex_cube;
4024 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4025 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4026 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4027 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4029 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4030 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4031 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4032 info->tex_type = tex_cube;
4033 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4034 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4035 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4036 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4038 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4039 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4040 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4041 info->tex_type = tex_cube;
4042 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4043 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4044 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4045 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4047 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4048 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4049 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4050 info->tex_type = tex_cube;
4051 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4052 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4053 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4054 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4056 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4057 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4058 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4059 info->tex_type = tex_cube;
4060 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4061 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4062 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4063 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4065 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4066 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4067 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4068 info->tex_type = tex_cube;
4069 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4070 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4071 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4072 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4076 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4078 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4079 struct depth_blt_info info;
4080 GLint old_binding = 0;
4082 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4084 glDisable(GL_CULL_FACE);
4085 glEnable(GL_BLEND);
4086 glDisable(GL_ALPHA_TEST);
4087 glDisable(GL_SCISSOR_TEST);
4088 glDisable(GL_STENCIL_TEST);
4089 glEnable(GL_DEPTH_TEST);
4090 glDepthFunc(GL_ALWAYS);
4091 glDepthMask(GL_TRUE);
4092 glBlendFunc(GL_ZERO, GL_ONE);
4093 glViewport(0, 0, w, h);
4095 surface_get_depth_blt_info(target, w, h, &info);
4096 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4097 glGetIntegerv(info.binding, &old_binding);
4098 glBindTexture(info.bind_target, texture);
4100 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4102 glBegin(GL_TRIANGLE_STRIP);
4103 glTexCoord3fv(info.coords[0]);
4104 glVertex2f(-1.0f, -1.0f);
4105 glTexCoord3fv(info.coords[1]);
4106 glVertex2f(1.0f, -1.0f);
4107 glTexCoord3fv(info.coords[2]);
4108 glVertex2f(-1.0f, 1.0f);
4109 glTexCoord3fv(info.coords[3]);
4110 glVertex2f(1.0f, 1.0f);
4111 glEnd();
4113 glBindTexture(info.bind_target, old_binding);
4115 glPopAttrib();
4117 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4120 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4121 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4123 TRACE("(%p) New location %#x\n", This, location);
4125 if (location & ~SFLAG_DS_LOCATIONS) {
4126 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4129 This->Flags &= ~SFLAG_DS_LOCATIONS;
4130 This->Flags |= location;
4133 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4134 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4135 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4137 TRACE("(%p) New location %#x\n", This, location);
4139 /* TODO: Make this work for modes other than FBO */
4140 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4142 if (This->Flags & location) {
4143 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4144 return;
4147 if (This->current_renderbuffer) {
4148 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4149 return;
4152 if (location == SFLAG_DS_OFFSCREEN) {
4153 if (This->Flags & SFLAG_DS_ONSCREEN) {
4154 GLint old_binding = 0;
4155 GLenum bind_target;
4157 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4159 ENTER_GL();
4161 if (!device->depth_blt_texture) {
4162 glGenTextures(1, &device->depth_blt_texture);
4165 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4166 * directly on the FBO texture. That's because we need to flip. */
4167 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4168 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4169 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4170 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4171 } else {
4172 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4173 bind_target = GL_TEXTURE_2D;
4175 glBindTexture(bind_target, device->depth_blt_texture);
4176 glCopyTexImage2D(bind_target,
4177 This->glDescription.level,
4178 This->resource.format_desc->glInternal,
4181 This->currentDesc.Width,
4182 This->currentDesc.Height,
4184 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4185 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4186 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4187 glBindTexture(bind_target, old_binding);
4189 /* Setup the destination */
4190 if (!device->depth_blt_rb) {
4191 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4192 checkGLcall("glGenRenderbuffersEXT");
4194 if (device->depth_blt_rb_w != This->currentDesc.Width
4195 || device->depth_blt_rb_h != This->currentDesc.Height) {
4196 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4197 checkGLcall("glBindRenderbufferEXT");
4198 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4199 checkGLcall("glRenderbufferStorageEXT");
4200 device->depth_blt_rb_w = This->currentDesc.Width;
4201 device->depth_blt_rb_h = This->currentDesc.Height;
4204 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4205 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4206 checkGLcall("glFramebufferRenderbufferEXT");
4207 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4209 /* Do the actual blit */
4210 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4211 checkGLcall("depth_blt");
4213 if (device->activeContext->current_fbo) {
4214 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4215 } else {
4216 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4217 checkGLcall("glBindFramebuffer()");
4220 LEAVE_GL();
4221 } else {
4222 FIXME("No up to date depth stencil location\n");
4224 } else if (location == SFLAG_DS_ONSCREEN) {
4225 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4226 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4228 ENTER_GL();
4230 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4231 checkGLcall("glBindFramebuffer()");
4232 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4233 checkGLcall("depth_blt");
4235 if (device->activeContext->current_fbo) {
4236 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4237 checkGLcall("glBindFramebuffer()");
4240 LEAVE_GL();
4241 } else {
4242 FIXME("No up to date depth stencil location\n");
4244 } else {
4245 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4248 This->Flags |= location;
4251 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4252 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4253 IWineD3DBaseTexture *texture;
4254 IWineD3DSurfaceImpl *overlay;
4256 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4257 persistent ? "TRUE" : "FALSE");
4259 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4260 if (This->Flags & SFLAG_SWAPCHAIN)
4262 TRACE("Surface %p is an onscreen surface\n", iface);
4263 } else {
4264 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4265 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4269 if(persistent) {
4270 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4271 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4272 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4273 TRACE("Passing to container\n");
4274 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4275 IWineD3DBaseTexture_Release(texture);
4278 This->Flags &= ~SFLAG_LOCATIONS;
4279 This->Flags |= flag;
4281 /* Redraw emulated overlays, if any */
4282 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4283 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4284 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4287 } else {
4288 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4289 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4290 TRACE("Passing to container\n");
4291 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4292 IWineD3DBaseTexture_Release(texture);
4295 This->Flags &= ~flag;
4298 if(!(This->Flags & SFLAG_LOCATIONS)) {
4299 ERR("%p: Surface does not have any up to date location\n", This);
4303 struct coords {
4304 GLfloat x, y, z;
4307 struct float_rect
4309 float l;
4310 float t;
4311 float r;
4312 float b;
4315 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4317 f->l = ((r->left * 2.0f) / w) - 1.0f;
4318 f->t = ((r->top * 2.0f) / h) - 1.0f;
4319 f->r = ((r->right * 2.0f) / w) - 1.0f;
4320 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4323 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4324 struct coords coords[4];
4325 RECT rect;
4326 IWineD3DSwapChain *swapchain;
4327 IWineD3DBaseTexture *texture;
4328 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4329 GLenum bind_target;
4330 struct float_rect f;
4332 if(rect_in) {
4333 rect = *rect_in;
4334 } else {
4335 rect.left = 0;
4336 rect.top = 0;
4337 rect.right = This->currentDesc.Width;
4338 rect.bottom = This->currentDesc.Height;
4341 switch(This->glDescription.target)
4343 case GL_TEXTURE_2D:
4344 bind_target = GL_TEXTURE_2D;
4346 coords[0].x = (float)rect.left / This->pow2Width;
4347 coords[0].y = (float)rect.top / This->pow2Height;
4348 coords[0].z = 0;
4350 coords[1].x = (float)rect.left / This->pow2Width;
4351 coords[1].y = (float)rect.bottom / This->pow2Height;
4352 coords[1].z = 0;
4354 coords[2].x = (float)rect.right / This->pow2Width;
4355 coords[2].y = (float)rect.bottom / This->pow2Height;
4356 coords[2].z = 0;
4358 coords[3].x = (float)rect.right / This->pow2Width;
4359 coords[3].y = (float)rect.top / This->pow2Height;
4360 coords[3].z = 0;
4361 break;
4363 case GL_TEXTURE_RECTANGLE_ARB:
4364 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4365 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4366 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4367 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4368 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4369 break;
4371 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4372 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4373 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4374 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4375 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4376 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4377 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4378 break;
4380 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4381 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4382 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4383 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4384 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4385 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4386 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4387 break;
4389 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4390 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4391 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4392 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4393 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4394 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4395 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4396 break;
4398 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4399 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4400 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4401 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4402 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4403 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4404 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4405 break;
4407 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4408 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4409 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4410 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4411 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4412 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4413 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4414 break;
4416 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4417 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4418 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4419 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4420 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4421 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4422 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4423 break;
4425 default:
4426 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4427 return;
4430 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4431 ENTER_GL();
4433 glEnable(bind_target);
4434 checkGLcall("glEnable(bind_target)");
4435 glBindTexture(bind_target, This->glDescription.textureName);
4436 checkGLcall("bind_target, This->glDescription.textureName)");
4437 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4438 checkGLcall("glTexParameteri");
4439 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4440 checkGLcall("glTexParameteri");
4442 if (device->render_offscreen)
4444 LONG tmp = rect.top;
4445 rect.top = rect.bottom;
4446 rect.bottom = tmp;
4449 glBegin(GL_QUADS);
4450 glTexCoord3fv(&coords[0].x);
4451 glVertex2i(rect.left, rect.top);
4453 glTexCoord3fv(&coords[1].x);
4454 glVertex2i(rect.left, rect.bottom);
4456 glTexCoord3fv(&coords[2].x);
4457 glVertex2i(rect.right, rect.bottom);
4459 glTexCoord3fv(&coords[3].x);
4460 glVertex2i(rect.right, rect.top);
4461 glEnd();
4462 checkGLcall("glEnd");
4464 glDisable(bind_target);
4465 checkGLcall("glDisable(bind_target)");
4467 LEAVE_GL();
4469 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4471 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4472 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4473 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4474 glFlush();
4476 IWineD3DSwapChain_Release(swapchain);
4477 } else {
4478 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4479 * reset properly next draw
4481 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4483 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4484 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4485 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4486 IWineD3DBaseTexture_Release(texture);
4491 /*****************************************************************************
4492 * IWineD3DSurface::LoadLocation
4494 * Copies the current surface data from wherever it is to the requested
4495 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4496 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4497 * multiple locations, the gl texture is preferred over the drawable, which is
4498 * preferred over system memory. The PBO counts as system memory. If rect is
4499 * not NULL, only the specified rectangle is copied (only supported for
4500 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4501 * location is marked up to date after the copy.
4503 * Parameters:
4504 * flag: Surface location flag to be updated
4505 * rect: rectangle to be copied
4507 * Returns:
4508 * WINED3D_OK on success
4509 * WINED3DERR_DEVICELOST on an internal error
4511 *****************************************************************************/
4512 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4513 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4514 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4515 GLenum format, internal, type;
4516 CONVERT_TYPES convert;
4517 int bpp;
4518 int width, pitch, outpitch;
4519 BYTE *mem;
4520 BOOL drawable_read_ok = TRUE;
4522 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4523 if (This->Flags & SFLAG_SWAPCHAIN)
4525 TRACE("Surface %p is an onscreen surface\n", iface);
4526 } else {
4527 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4528 * Prefer SFLAG_INTEXTURE. */
4529 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4530 drawable_read_ok = FALSE;
4534 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4535 if(rect) {
4536 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4539 if(This->Flags & flag) {
4540 TRACE("Location already up to date\n");
4541 return WINED3D_OK;
4544 if(!(This->Flags & SFLAG_LOCATIONS)) {
4545 ERR("%p: Surface does not have any up to date location\n", This);
4546 This->Flags |= SFLAG_LOST;
4547 return WINED3DERR_DEVICELOST;
4550 if(flag == SFLAG_INSYSMEM) {
4551 surface_prepare_system_memory(This);
4553 /* Download the surface to system memory */
4554 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4555 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4556 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4558 surface_download_data(This);
4559 } else {
4560 read_from_framebuffer(This, rect,
4561 This->resource.allocatedMemory,
4562 IWineD3DSurface_GetPitch(iface));
4564 } else if(flag == SFLAG_INDRAWABLE) {
4565 if(This->Flags & SFLAG_INTEXTURE) {
4566 surface_blt_to_drawable(This, rect);
4567 } else {
4568 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4569 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4570 * values, otherwise we get incorrect values in the target. For now go the slow way
4571 * via a system memory copy
4573 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4576 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4578 /* The width is in 'length' not in bytes */
4579 width = This->currentDesc.Width;
4580 pitch = IWineD3DSurface_GetPitch(iface);
4582 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4583 * but it isn't set (yet) in all cases it is getting called. */
4584 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4585 TRACE("Removing the pbo attached to surface %p\n", This);
4586 surface_remove_pbo(This);
4589 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4590 int height = This->currentDesc.Height;
4592 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4593 outpitch = width * bpp;
4594 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4596 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4597 if(!mem) {
4598 ERR("Out of memory %d, %d!\n", outpitch, height);
4599 return WINED3DERR_OUTOFVIDEOMEMORY;
4601 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4603 This->Flags |= SFLAG_CONVERTED;
4604 } else {
4605 This->Flags &= ~SFLAG_CONVERTED;
4606 mem = This->resource.allocatedMemory;
4609 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4611 /* Don't delete PBO memory */
4612 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4613 HeapFree(GetProcessHeap(), 0, mem);
4615 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4616 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4617 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4618 } else { /* Upload from system memory */
4619 BOOL srgb = flag == SFLAG_INSRGBTEX;
4620 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4621 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4623 if(srgb) {
4624 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4625 /* Performance warning ... */
4626 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4627 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4629 } else {
4630 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4631 /* Performance warning ... */
4632 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4633 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4637 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4638 surface_bind_and_dirtify(This, srgb);
4640 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4641 This->Flags |= SFLAG_GLCKEY;
4642 This->glCKey = This->SrcBltCKey;
4644 else This->Flags &= ~SFLAG_GLCKEY;
4646 /* The width is in 'length' not in bytes */
4647 width = This->currentDesc.Width;
4648 pitch = IWineD3DSurface_GetPitch(iface);
4650 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4651 * but it isn't set (yet) in all cases it is getting called. */
4652 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4653 TRACE("Removing the pbo attached to surface %p\n", This);
4654 surface_remove_pbo(This);
4657 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4658 int height = This->currentDesc.Height;
4660 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4661 outpitch = width * bpp;
4662 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4664 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4665 if(!mem) {
4666 ERR("Out of memory %d, %d!\n", outpitch, height);
4667 return WINED3DERR_OUTOFVIDEOMEMORY;
4669 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4671 This->Flags |= SFLAG_CONVERTED;
4673 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4674 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4676 d3dfmt_p8_upload_palette(iface, convert);
4677 This->Flags &= ~SFLAG_CONVERTED;
4678 mem = This->resource.allocatedMemory;
4679 } else {
4680 This->Flags &= ~SFLAG_CONVERTED;
4681 mem = This->resource.allocatedMemory;
4684 /* Make sure the correct pitch is used */
4685 ENTER_GL();
4686 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4687 LEAVE_GL();
4689 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4690 TRACE("non power of two support\n");
4691 if(!(This->Flags & alloc_flag)) {
4692 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4693 This->Flags |= alloc_flag;
4695 if (mem || (This->Flags & SFLAG_PBO)) {
4696 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4698 } else {
4699 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4700 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4702 if(!(This->Flags & alloc_flag)) {
4703 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4704 This->Flags |= alloc_flag;
4706 if (mem || (This->Flags & SFLAG_PBO)) {
4707 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4711 /* Restore the default pitch */
4712 ENTER_GL();
4713 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4714 LEAVE_GL();
4716 /* Don't delete PBO memory */
4717 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4718 HeapFree(GetProcessHeap(), 0, mem);
4722 if(rect == NULL) {
4723 This->Flags |= flag;
4726 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4727 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4728 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4729 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4732 return WINED3D_OK;
4735 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4737 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4738 IWineD3DSwapChain *swapchain = NULL;
4740 /* Update the drawable size method */
4741 if(container) {
4742 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4744 if(swapchain) {
4745 This->get_drawable_size = get_drawable_size_swapchain;
4746 IWineD3DSwapChain_Release(swapchain);
4747 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4748 switch(wined3d_settings.offscreen_rendering_mode) {
4749 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4750 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4751 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4755 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4758 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4759 return SURFACE_OPENGL;
4762 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4763 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4764 HRESULT hr;
4766 /* If there's no destination surface there is nothing to do */
4767 if(!This->overlay_dest) return WINED3D_OK;
4769 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4770 * update the overlay. Prevent an endless recursion
4772 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4773 return WINED3D_OK;
4775 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4776 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4777 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4778 NULL, WINED3DTEXF_LINEAR);
4779 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4781 return hr;
4784 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4786 /* IUnknown */
4787 IWineD3DBaseSurfaceImpl_QueryInterface,
4788 IWineD3DBaseSurfaceImpl_AddRef,
4789 IWineD3DSurfaceImpl_Release,
4790 /* IWineD3DResource */
4791 IWineD3DBaseSurfaceImpl_GetParent,
4792 IWineD3DBaseSurfaceImpl_GetDevice,
4793 IWineD3DBaseSurfaceImpl_SetPrivateData,
4794 IWineD3DBaseSurfaceImpl_GetPrivateData,
4795 IWineD3DBaseSurfaceImpl_FreePrivateData,
4796 IWineD3DBaseSurfaceImpl_SetPriority,
4797 IWineD3DBaseSurfaceImpl_GetPriority,
4798 IWineD3DSurfaceImpl_PreLoad,
4799 IWineD3DSurfaceImpl_UnLoad,
4800 IWineD3DBaseSurfaceImpl_GetType,
4801 /* IWineD3DSurface */
4802 IWineD3DBaseSurfaceImpl_GetContainer,
4803 IWineD3DBaseSurfaceImpl_GetDesc,
4804 IWineD3DSurfaceImpl_LockRect,
4805 IWineD3DSurfaceImpl_UnlockRect,
4806 IWineD3DSurfaceImpl_GetDC,
4807 IWineD3DSurfaceImpl_ReleaseDC,
4808 IWineD3DSurfaceImpl_Flip,
4809 IWineD3DSurfaceImpl_Blt,
4810 IWineD3DBaseSurfaceImpl_GetBltStatus,
4811 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4812 IWineD3DBaseSurfaceImpl_IsLost,
4813 IWineD3DBaseSurfaceImpl_Restore,
4814 IWineD3DSurfaceImpl_BltFast,
4815 IWineD3DBaseSurfaceImpl_GetPalette,
4816 IWineD3DBaseSurfaceImpl_SetPalette,
4817 IWineD3DSurfaceImpl_RealizePalette,
4818 IWineD3DBaseSurfaceImpl_SetColorKey,
4819 IWineD3DBaseSurfaceImpl_GetPitch,
4820 IWineD3DSurfaceImpl_SetMem,
4821 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4822 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4823 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4824 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4825 IWineD3DBaseSurfaceImpl_SetClipper,
4826 IWineD3DBaseSurfaceImpl_GetClipper,
4827 /* Internal use: */
4828 IWineD3DSurfaceImpl_LoadTexture,
4829 IWineD3DSurfaceImpl_BindTexture,
4830 IWineD3DSurfaceImpl_SaveSnapshot,
4831 IWineD3DSurfaceImpl_SetContainer,
4832 IWineD3DSurfaceImpl_GetGlDesc,
4833 IWineD3DBaseSurfaceImpl_GetData,
4834 IWineD3DSurfaceImpl_SetFormat,
4835 IWineD3DSurfaceImpl_PrivateSetup,
4836 IWineD3DSurfaceImpl_ModifyLocation,
4837 IWineD3DSurfaceImpl_LoadLocation,
4838 IWineD3DSurfaceImpl_GetImplType,
4839 IWineD3DSurfaceImpl_DrawOverlay
4841 #undef GLINFO_LOCATION
4843 #define GLINFO_LOCATION device->adapter->gl_info
4844 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4845 static void ffp_blit_free(IWineD3DDevice *iface) { }
4847 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
4848 GLenum textype, UINT width, UINT height)
4850 glEnable(textype);
4851 checkGLcall("glEnable(textype)");
4852 return WINED3D_OK;
4855 static void ffp_blit_unset(IWineD3DDevice *iface) {
4856 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4857 glDisable(GL_TEXTURE_2D);
4858 checkGLcall("glDisable(GL_TEXTURE_2D)");
4859 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4860 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4861 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4863 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4864 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4865 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4869 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4871 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4873 TRACE("Checking support for fixup:\n");
4874 dump_color_fixup_desc(fixup);
4877 /* We only support identity conversions. */
4878 if (is_identity_fixup(fixup))
4880 TRACE("[OK]\n");
4881 return TRUE;
4884 TRACE("[FAILED]\n");
4885 return FALSE;
4888 const struct blit_shader ffp_blit = {
4889 ffp_blit_alloc,
4890 ffp_blit_free,
4891 ffp_blit_set,
4892 ffp_blit_unset,
4893 ffp_blit_color_fixup_supported