wined3d: Use rev_tex_unit_map instead of assuming there's a 1:1 mapping between sampl...
[wine/gsoc_dplay.git] / dlls / wined3d / surface.c
blob53070f286c5cc82f9874c1fc401ceb010d4adad2
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 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
42 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
43 int active_sampler;
45 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
46 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
47 * gl states. The current texture unit should always be a valid one.
49 * TODO: Track the current active texture per GL context instead of using glGet
51 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
52 GLint active_texture;
53 ENTER_GL();
54 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
55 LEAVE_GL();
56 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
57 } else {
58 active_sampler = 0;
61 if (active_sampler != -1) {
62 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
64 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
67 /* This function checks if the primary render target uses the 8bit paletted format. */
68 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
70 if (device->render_targets && device->render_targets[0]) {
71 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
72 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
73 return TRUE;
75 return FALSE;
78 /* This call just downloads data, the caller is responsible for activating the
79 * right context and binding the correct texture. */
80 static void surface_download_data(IWineD3DSurfaceImpl *This) {
81 if (0 == This->glDescription.textureName) {
82 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
83 return;
86 /* Only support read back of converted P8 surfaces */
87 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
88 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
89 return;
92 ENTER_GL();
94 if (This->resource.format == WINED3DFMT_DXT1 ||
95 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
96 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
97 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
98 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
99 } else {
100 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
101 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
103 if(This->Flags & SFLAG_PBO) {
104 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
105 checkGLcall("glBindBufferARB");
106 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
107 checkGLcall("glGetCompressedTexImageARB()");
108 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
109 checkGLcall("glBindBufferARB");
110 } else {
111 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
112 checkGLcall("glGetCompressedTexImageARB()");
115 LEAVE_GL();
116 } else {
117 void *mem;
118 GLenum format = This->glDescription.glFormat;
119 GLenum type = This->glDescription.glType;
120 int src_pitch = 0;
121 int dst_pitch = 0;
123 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
124 if(This->resource.format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice)) {
125 format = GL_ALPHA;
126 type = GL_UNSIGNED_BYTE;
129 if (This->Flags & SFLAG_NONPOW2) {
130 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
131 src_pitch = This->bytesPerPixel * This->pow2Width;
132 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
133 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
134 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
135 } else {
136 mem = This->resource.allocatedMemory;
139 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
140 format, type, mem);
142 if(This->Flags & SFLAG_PBO) {
143 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
144 checkGLcall("glBindBufferARB");
146 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
147 type, NULL);
148 checkGLcall("glGetTexImage()");
150 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
151 checkGLcall("glBindBufferARB");
152 } else {
153 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
154 type, mem);
155 checkGLcall("glGetTexImage()");
157 LEAVE_GL();
159 if (This->Flags & SFLAG_NONPOW2) {
160 LPBYTE src_data, dst_data;
161 int y;
163 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
164 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
165 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
167 * We're doing this...
169 * instead of boxing the texture :
170 * |<-texture width ->| -->pow2width| /\
171 * |111111111111111111| | |
172 * |222 Texture 222222| boxed empty | texture height
173 * |3333 Data 33333333| | |
174 * |444444444444444444| | \/
175 * ----------------------------------- |
176 * | boxed empty | boxed empty | pow2height
177 * | | | \/
178 * -----------------------------------
181 * we're repacking the data to the expected texture width
183 * |<-texture width ->| -->pow2width| /\
184 * |111111111111111111222222222222222| |
185 * |222333333333333333333444444444444| texture height
186 * |444444 | |
187 * | | \/
188 * | | |
189 * | empty | pow2height
190 * | | \/
191 * -----------------------------------
193 * == is the same as
195 * |<-texture width ->| /\
196 * |111111111111111111|
197 * |222222222222222222|texture height
198 * |333333333333333333|
199 * |444444444444444444| \/
200 * --------------------
202 * this also means that any references to allocatedMemory should work with the data as if were a
203 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
205 * internally the texture is still stored in a boxed format so any references to textureName will
206 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
208 * Performance should not be an issue, because applications normally do not lock the surfaces when
209 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
210 * and doesn't have to be re-read.
212 src_data = mem;
213 dst_data = This->resource.allocatedMemory;
214 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
215 for (y = 1 ; y < This->currentDesc.Height; y++) {
216 /* skip the first row */
217 src_data += src_pitch;
218 dst_data += dst_pitch;
219 memcpy(dst_data, src_data, dst_pitch);
222 HeapFree(GetProcessHeap(), 0, mem);
226 /* Surface has now been downloaded */
227 This->Flags |= SFLAG_INSYSMEM;
230 /* This call just uploads data, the caller is responsible for activating the
231 * right context and binding the correct texture. */
232 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
233 if (This->resource.format == WINED3DFMT_DXT1 ||
234 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
235 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
236 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
237 FIXME("Using DXT1/3/5 without advertized support\n");
238 } else {
239 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
240 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
241 * function uses glCompressedTexImage2D instead of the SubImage call
243 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
244 ENTER_GL();
246 if(This->Flags & SFLAG_PBO) {
247 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
248 checkGLcall("glBindBufferARB");
249 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
251 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
252 width, height, 0 /* border */, This->resource.size, NULL));
253 checkGLcall("glCompressedTexSubImage2D");
255 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
256 checkGLcall("glBindBufferARB");
257 } else {
258 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
259 width, height, 0 /* border */, This->resource.size, data));
260 checkGLcall("glCompressedTexSubImage2D");
262 LEAVE_GL();
264 } else {
265 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
266 ENTER_GL();
268 if(This->Flags & SFLAG_PBO) {
269 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
270 checkGLcall("glBindBufferARB");
271 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
273 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
274 checkGLcall("glTexSubImage2D");
276 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
277 checkGLcall("glBindBufferARB");
279 else {
280 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
281 checkGLcall("glTexSubImage2D");
284 LEAVE_GL();
288 /* This call just allocates the texture, the caller is responsible for
289 * activating the right context and binding the correct texture. */
290 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
291 BOOL enable_client_storage = FALSE;
292 BYTE *mem = NULL;
294 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
295 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
297 if (This->resource.format == WINED3DFMT_DXT1 ||
298 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
299 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
300 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
301 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
303 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
304 * once, unfortunately
306 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
307 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
308 This->Flags |= SFLAG_CLIENT;
309 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
310 ENTER_GL();
311 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
312 width, height, 0 /* border */, This->resource.size, mem));
313 LEAVE_GL();
316 return;
319 ENTER_GL();
321 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
322 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
323 /* In some cases we want to disable client storage.
324 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
325 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
326 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
327 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
328 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
330 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
331 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
332 This->Flags &= ~SFLAG_CLIENT;
333 enable_client_storage = TRUE;
334 } else {
335 This->Flags |= SFLAG_CLIENT;
337 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
338 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
340 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
343 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
344 checkGLcall("glTexImage2D");
346 if(enable_client_storage) {
347 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
348 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
350 LEAVE_GL();
352 This->Flags |= SFLAG_ALLOCATED;
355 /* In D3D the depth stencil dimensions have to be greater than or equal to the
356 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
357 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
358 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
359 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
360 renderbuffer_entry_t *entry;
361 GLuint renderbuffer = 0;
362 unsigned int src_width, src_height;
364 src_width = This->pow2Width;
365 src_height = This->pow2Height;
367 /* A depth stencil smaller than the render target is not valid */
368 if (width > src_width || height > src_height) return;
370 /* Remove any renderbuffer set if the sizes match */
371 if (width == src_width && height == src_height) {
372 This->current_renderbuffer = NULL;
373 return;
376 /* Look if we've already got a renderbuffer of the correct dimensions */
377 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
378 if (entry->width == width && entry->height == height) {
379 renderbuffer = entry->id;
380 This->current_renderbuffer = entry;
381 break;
385 if (!renderbuffer) {
386 const GlPixelFormatDesc *glDesc;
387 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
389 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
390 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
391 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
393 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
394 entry->width = width;
395 entry->height = height;
396 entry->id = renderbuffer;
397 list_add_head(&This->renderbuffers, &entry->entry);
399 This->current_renderbuffer = entry;
402 checkGLcall("set_compatible_renderbuffer");
405 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
406 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
407 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
409 TRACE("(%p) : swapchain %p\n", This, swapchain);
411 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
412 TRACE("Returning GL_BACK\n");
413 return GL_BACK;
414 } else if (swapchain_impl->frontBuffer == iface) {
415 TRACE("Returning GL_FRONT\n");
416 return GL_FRONT;
419 FIXME("Higher back buffer, returning GL_BACK\n");
420 return GL_BACK;
423 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
424 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
425 ULONG ref = InterlockedDecrement(&This->resource.ref);
426 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
427 if (ref == 0) {
428 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
429 renderbuffer_entry_t *entry, *entry2;
430 TRACE("(%p) : cleaning up\n", This);
432 /* Need a context to destroy the texture. Use the currently active render target, but only if
433 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
434 * When destroying the primary rt, Uninit3D will activate a context before doing anything
436 if(device->render_targets && device->render_targets[0]) {
437 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
440 ENTER_GL();
441 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
442 TRACE("Deleting texture %d\n", This->glDescription.textureName);
443 glDeleteTextures(1, &This->glDescription.textureName);
446 if(This->Flags & SFLAG_PBO) {
447 /* Delete the PBO */
448 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
451 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
452 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
453 HeapFree(GetProcessHeap(), 0, entry);
455 LEAVE_GL();
457 if(This->Flags & SFLAG_DIBSECTION) {
458 /* Release the DC */
459 SelectObject(This->hDC, This->dib.holdbitmap);
460 DeleteDC(This->hDC);
461 /* Release the DIB section */
462 DeleteObject(This->dib.DIBsection);
463 This->dib.bitmap_data = NULL;
464 This->resource.allocatedMemory = NULL;
466 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
468 HeapFree(GetProcessHeap(), 0, This->palette9);
470 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
471 if(iface == device->ddraw_primary)
472 device->ddraw_primary = NULL;
474 TRACE("(%p) Released\n", This);
475 HeapFree(GetProcessHeap(), 0, This);
478 return ref;
481 /* ****************************************************
482 IWineD3DSurface IWineD3DResource parts follow
483 **************************************************** */
485 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
486 /* TODO: check for locks */
487 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
488 IWineD3DBaseTexture *baseTexture = NULL;
489 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
491 TRACE("(%p)Checking to see if the container is a base texture\n", This);
492 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
493 TRACE("Passing to container\n");
494 IWineD3DBaseTexture_PreLoad(baseTexture);
495 IWineD3DBaseTexture_Release(baseTexture);
496 } else {
497 TRACE("(%p) : About to load surface\n", This);
499 if(!device->isInDraw) {
500 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
503 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
504 if(palette9_changed(This)) {
505 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
506 /* TODO: This is not necessarily needed with hw palettized texture support */
507 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
508 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
509 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
512 ENTER_GL();
513 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
514 if (!This->glDescription.level) {
515 if (!This->glDescription.textureName) {
516 glGenTextures(1, &This->glDescription.textureName);
517 checkGLcall("glGenTextures");
518 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
520 glBindTexture(This->glDescription.target, This->glDescription.textureName);
521 checkGLcall("glBindTexture");
522 LEAVE_GL();
523 IWineD3DSurface_LoadTexture(iface, FALSE);
524 /* This is where we should be reducing the amount of GLMemoryUsed */
525 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
526 /* assume this is a coding error not a real error for now */
527 FIXME("Mipmap surface has a glTexture bound to it!\n");
528 LEAVE_GL();
530 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
531 /* Tell opengl to try and keep this texture in video ram (well mostly) */
532 GLclampf tmp;
533 tmp = 0.9f;
534 ENTER_GL();
535 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
536 LEAVE_GL();
539 return;
542 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
543 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
544 This->resource.allocatedMemory =
545 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
547 ENTER_GL();
548 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
549 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
550 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
551 checkGLcall("glGetBufferSubData");
552 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
553 checkGLcall("glDeleteBuffers");
554 LEAVE_GL();
556 This->pbo = 0;
557 This->Flags &= ~SFLAG_PBO;
560 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
561 IWineD3DBaseTexture *texture = NULL;
562 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
563 renderbuffer_entry_t *entry, *entry2;
564 TRACE("(%p)\n", iface);
566 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
567 /* Default pool resources are supposed to be destroyed before Reset is called.
568 * Implicit resources stay however. So this means we have an implicit render target
569 * or depth stencil. The content may be destroyed, but we still have to tear down
570 * opengl resources, so we cannot leave early.
572 * Put the most up to date surface location into the drawable. D3D-wise this content
573 * is undefined, so it would be nowhere, but that would make the location management
574 * more complicated. The drawable is a sane location, because if we mark sysmem or
575 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
576 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
577 * sysmem copy here.
579 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
580 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
581 } else {
582 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
584 } else {
585 /* Load the surface into system memory */
586 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
587 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
589 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
590 This->Flags &= ~SFLAG_ALLOCATED;
592 /* Destroy PBOs, but load them into real sysmem before */
593 if(This->Flags & SFLAG_PBO) {
594 surface_remove_pbo(This);
597 /* Destroy fbo render buffers. This is needed for implicit render targets, for
598 * all application-created targets the application has to release the surface
599 * before calling _Reset
601 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
602 ENTER_GL();
603 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
604 LEAVE_GL();
605 list_remove(&entry->entry);
606 HeapFree(GetProcessHeap(), 0, entry);
608 list_init(&This->renderbuffers);
609 This->current_renderbuffer = NULL;
611 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
612 * destroy it
614 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
615 if(!texture) {
616 ENTER_GL();
617 glDeleteTextures(1, &This->glDescription.textureName);
618 This->glDescription.textureName = 0;
619 LEAVE_GL();
620 } else {
621 IWineD3DBaseTexture_Release(texture);
623 return;
626 /* ******************************************************
627 IWineD3DSurface IWineD3DSurface parts follow
628 ****************************************************** */
630 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
631 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
632 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
633 if (This->glDescription.textureName == 0 && textureName != 0) {
634 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
635 if((This->Flags & SFLAG_LOCATIONS) == 0) {
636 /* In 1.0-rc4 and earlier, AddDirtyRect was called in the place of this if condition.
637 * This had the problem that a correctly set INDRAWABLE flag was removed if the PreLoad
638 * during the offscreen rendering readback triggered the creation of the GL texture.
639 * The change intended to keep the INDRAWABLE intact. To prevent unintended side effects
640 * before release, set the INSYSMEM flag like the old AddDirtyRect did.
642 WARN("Wine 1.0 safety path hit\n");
643 This->Flags |= SFLAG_INSYSMEM;
646 if(target == GL_TEXTURE_RECTANGLE_ARB && This->glDescription.target != target) {
647 This->Flags &= ~SFLAG_NORMCOORD;
648 } else if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB && target != GL_TEXTURE_RECTANGLE_ARB) {
649 This->Flags |= SFLAG_NORMCOORD;
651 This->glDescription.textureName = textureName;
652 This->glDescription.target = target;
653 This->Flags &= ~SFLAG_ALLOCATED;
656 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
657 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
658 TRACE("(%p) : returning %p\n", This, &This->glDescription);
659 *glDescription = &This->glDescription;
662 /* TODO: think about moving this down to resource? */
663 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
664 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
665 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the future */
666 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
667 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
669 return (CONST void*)(This->resource.allocatedMemory);
672 /* Read the framebuffer back into the surface */
673 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
674 IWineD3DSwapChainImpl *swapchain;
675 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
676 BYTE *mem;
677 GLint fmt;
678 GLint type;
679 BYTE *row, *top, *bottom;
680 int i;
681 BOOL bpp;
682 RECT local_rect;
683 BOOL srcIsUpsideDown;
685 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
686 static BOOL warned = FALSE;
687 if(!warned) {
688 ERR("The application tries to lock the render target, but render target locking is disabled\n");
689 warned = TRUE;
691 return;
694 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
695 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
696 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
697 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
698 * context->last_was_blit set on the unlock.
700 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
701 ENTER_GL();
703 /* Select the correct read buffer, and give some debug output.
704 * There is no need to keep track of the current read buffer or reset it, every part of the code
705 * that reads sets the read buffer as desired.
707 if(!swapchain) {
708 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
709 * Read from the back buffer
711 TRACE("Locking offscreen render target\n");
712 glReadBuffer(myDevice->offscreenBuffer);
713 srcIsUpsideDown = TRUE;
714 } else {
715 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
716 TRACE("Locking %#x buffer\n", buffer);
717 glReadBuffer(buffer);
718 checkGLcall("glReadBuffer");
720 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
721 srcIsUpsideDown = FALSE;
724 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
725 if(!rect) {
726 local_rect.left = 0;
727 local_rect.top = 0;
728 local_rect.right = This->currentDesc.Width;
729 local_rect.bottom = This->currentDesc.Height;
730 } else {
731 local_rect = *rect;
733 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
735 switch(This->resource.format)
737 case WINED3DFMT_P8:
739 if(primary_render_target_is_p8(myDevice)) {
740 /* In case of P8 render targets the index is stored in the alpha component */
741 fmt = GL_ALPHA;
742 type = GL_UNSIGNED_BYTE;
743 mem = dest;
744 bpp = This->bytesPerPixel;
745 } else {
746 /* GL can't return palettized data, so read ARGB pixels into a
747 * separate block of memory and convert them into palettized format
748 * in software. Slow, but if the app means to use palettized render
749 * targets and locks it...
751 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
752 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
753 * for the color channels when palettizing the colors.
755 fmt = GL_RGB;
756 type = GL_UNSIGNED_BYTE;
757 pitch *= 3;
758 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
759 if(!mem) {
760 ERR("Out of memory\n");
761 LEAVE_GL();
762 return;
764 bpp = This->bytesPerPixel * 3;
767 break;
769 default:
770 mem = dest;
771 fmt = This->glDescription.glFormat;
772 type = This->glDescription.glType;
773 bpp = This->bytesPerPixel;
776 if(This->Flags & SFLAG_PBO) {
777 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
778 checkGLcall("glBindBufferARB");
781 glReadPixels(local_rect.left, local_rect.top,
782 local_rect.right - local_rect.left,
783 local_rect.bottom - local_rect.top,
784 fmt, type, mem);
785 vcheckGLcall("glReadPixels");
787 if(This->Flags & SFLAG_PBO) {
788 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
789 checkGLcall("glBindBufferARB");
791 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
792 * to get a pointer to it and perform the flipping in software. This is a lot
793 * faster than calling glReadPixels for each line. In case we want more speed
794 * we should rerender it flipped in a FBO and read the data back from the FBO. */
795 if(!srcIsUpsideDown) {
796 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
797 checkGLcall("glBindBufferARB");
799 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
800 checkGLcall("glMapBufferARB");
804 /* TODO: Merge this with the palettization loop below for P8 targets */
805 if(!srcIsUpsideDown) {
806 UINT len, off;
807 /* glReadPixels returns the image upside down, and there is no way to prevent this.
808 Flip the lines in software */
809 len = (local_rect.right - local_rect.left) * bpp;
810 off = local_rect.left * bpp;
812 row = HeapAlloc(GetProcessHeap(), 0, len);
813 if(!row) {
814 ERR("Out of memory\n");
815 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
816 LEAVE_GL();
817 return;
820 top = mem + pitch * local_rect.top;
821 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
822 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
823 memcpy(row, top + off, len);
824 memcpy(top + off, bottom + off, len);
825 memcpy(bottom + off, row, len);
826 top += pitch;
827 bottom -= pitch;
829 HeapFree(GetProcessHeap(), 0, row);
831 /* Unmap the temp PBO buffer */
832 if(This->Flags & SFLAG_PBO) {
833 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
834 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
838 LEAVE_GL();
840 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
841 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
842 * the same color but we have no choice.
843 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
845 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
846 PALETTEENTRY *pal = NULL;
847 DWORD width = pitch / 3;
848 int x, y, c;
850 if(This->palette) {
851 pal = This->palette->palents;
852 } else {
853 ERR("Palette is missing, cannot perform inverse palette lookup\n");
854 HeapFree(GetProcessHeap(), 0, mem);
855 return ;
858 for(y = local_rect.top; y < local_rect.bottom; y++) {
859 for(x = local_rect.left; x < local_rect.right; x++) {
860 /* start lines pixels */
861 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
862 BYTE *green = blue + 1;
863 BYTE *red = green + 1;
865 for(c = 0; c < 256; c++) {
866 if(*red == pal[c].peRed &&
867 *green == pal[c].peGreen &&
868 *blue == pal[c].peBlue)
870 *((BYTE *) dest + y * width + x) = c;
871 break;
876 HeapFree(GetProcessHeap(), 0, mem);
880 /* Read the framebuffer contents into a texture */
881 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
883 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
884 IWineD3DSwapChainImpl *swapchain;
885 int bpp;
886 GLenum format, internal, type;
887 CONVERT_TYPES convert;
888 GLint prevRead;
890 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
892 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
893 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
894 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
895 * states in the stateblock, and no driver was found yet that had bugs in that regard.
897 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
898 surface_bind_and_dirtify(This);
899 ENTER_GL();
901 glGetIntegerv(GL_READ_BUFFER, &prevRead);
903 /* Select the correct read buffer, and give some debug output.
904 * There is no need to keep track of the current read buffer or reset it, every part of the code
905 * that reads sets the read buffer as desired.
907 if(!swapchain) {
908 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
909 * Read from the back buffer
911 TRACE("Locking offscreen render target\n");
912 glReadBuffer(device->offscreenBuffer);
913 } else {
914 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
915 TRACE("Locking %#x buffer\n", buffer);
916 glReadBuffer(buffer);
917 checkGLcall("glReadBuffer");
919 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
922 if(!(This->Flags & SFLAG_ALLOCATED)) {
923 surface_allocate_surface(This, internal, This->pow2Width,
924 This->pow2Height, format, type);
927 clear_unused_channels(This);
929 /* If !SrcIsUpsideDown we should flip the surface.
930 * This can be done using glCopyTexSubImage2D but this
931 * is VERY slow, so don't do that. We should prevent
932 * this code from getting called in such cases or perhaps
933 * we can use FBOs */
935 glCopyTexSubImage2D(This->glDescription.target,
936 This->glDescription.level,
937 0, 0, 0, 0,
938 This->currentDesc.Width,
939 This->currentDesc.Height);
940 checkGLcall("glCopyTexSubImage2D");
942 glReadBuffer(prevRead);
943 vcheckGLcall("glReadBuffer");
945 LEAVE_GL();
946 TRACE("Updated target %d\n", This->glDescription.target);
949 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
950 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
951 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
952 * changed
954 if(!(This->Flags & SFLAG_DYNLOCK)) {
955 This->lockCount++;
956 /* MAXLOCKCOUNT is defined in wined3d_private.h */
957 if(This->lockCount > MAXLOCKCOUNT) {
958 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
959 This->Flags |= SFLAG_DYNLOCK;
963 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
964 * Also don't create a PBO for systemmem surfaces.
966 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
967 GLenum error;
968 ENTER_GL();
970 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
971 error = glGetError();
972 if(This->pbo == 0 || error != GL_NO_ERROR) {
973 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
976 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
978 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
979 checkGLcall("glBindBufferARB");
981 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
982 checkGLcall("glBufferDataARB");
984 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
985 checkGLcall("glBindBufferARB");
987 /* We don't need the system memory anymore and we can't even use it for PBOs */
988 if(!(This->Flags & SFLAG_CLIENT)) {
989 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
990 This->resource.heapMemory = NULL;
992 This->resource.allocatedMemory = NULL;
993 This->Flags |= SFLAG_PBO;
994 LEAVE_GL();
995 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
996 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
997 * or a pbo to map
999 if(!This->resource.heapMemory) {
1000 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1002 This->resource.allocatedMemory =
1003 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1004 if(This->Flags & SFLAG_INSYSMEM) {
1005 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1010 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1011 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1012 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1013 IWineD3DSwapChain *swapchain = NULL;
1015 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1017 /* This is also done in the base class, but we have to verify this before loading any data from
1018 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1019 * may interfere, and all other bad things may happen
1021 if (This->Flags & SFLAG_LOCKED) {
1022 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1023 return WINED3DERR_INVALIDCALL;
1025 This->Flags |= SFLAG_LOCKED;
1027 if (!(This->Flags & SFLAG_LOCKABLE))
1029 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1032 if (Flags & WINED3DLOCK_DISCARD) {
1033 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1034 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1035 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1036 This->Flags |= SFLAG_INSYSMEM;
1037 goto lock_end;
1040 if (This->Flags & SFLAG_INSYSMEM) {
1041 TRACE("Local copy is up to date, not downloading data\n");
1042 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1043 goto lock_end;
1046 /* Now download the surface content from opengl
1047 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1048 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1050 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1051 if(swapchain || iface == myDevice->render_targets[0]) {
1052 const RECT *pass_rect = pRect;
1054 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1055 * because most caller functions do not need that. So do that here
1057 if(pRect &&
1058 pRect->top == 0 &&
1059 pRect->left == 0 &&
1060 pRect->right == This->currentDesc.Width &&
1061 pRect->bottom == This->currentDesc.Height) {
1062 pass_rect = NULL;
1065 switch(wined3d_settings.rendertargetlock_mode) {
1066 case RTL_TEXDRAW:
1067 case RTL_TEXTEX:
1068 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1069 #if 0
1070 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1071 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1072 * This may be faster on some cards
1074 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1075 #endif
1076 /* drop through */
1078 case RTL_AUTO:
1079 case RTL_READDRAW:
1080 case RTL_READTEX:
1081 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1082 break;
1084 case RTL_DISABLE:
1085 break;
1087 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1089 } else if(iface == myDevice->stencilBufferTarget) {
1090 /** the depth stencil in openGL has a format of GL_FLOAT
1091 * which should be good for WINED3DFMT_D16_LOCKABLE
1092 * and WINED3DFMT_D16
1093 * it is unclear what format the stencil buffer is in except.
1094 * 'Each index is converted to fixed point...
1095 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1096 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1097 * glReadPixels(This->lockedRect.left,
1098 * This->lockedRect.bottom - j - 1,
1099 * This->lockedRect.right - This->lockedRect.left,
1100 * 1,
1101 * GL_DEPTH_COMPONENT,
1102 * type,
1103 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1105 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1106 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1107 * none of that is the case the problem is not in this function :-)
1108 ********************************************/
1109 FIXME("Depth stencil locking not supported yet\n");
1110 } else {
1111 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1112 TRACE("locking an ordinary surface\n");
1113 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1116 lock_end:
1117 if(This->Flags & SFLAG_PBO) {
1118 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1119 ENTER_GL();
1120 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1121 checkGLcall("glBindBufferARB");
1123 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1124 if(This->resource.allocatedMemory) {
1125 ERR("The surface already has PBO memory allocated!\n");
1128 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1129 checkGLcall("glMapBufferARB");
1131 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1133 checkGLcall("glBindBufferARB");
1135 LEAVE_GL();
1138 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1139 /* Don't dirtify */
1140 } else {
1141 IWineD3DBaseTexture *pBaseTexture;
1143 * Dirtify on lock
1144 * as seen in msdn docs
1146 IWineD3DSurface_AddDirtyRect(iface, pRect);
1148 /** Dirtify Container if needed */
1149 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1150 TRACE("Making container dirty\n");
1151 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1152 IWineD3DBaseTexture_Release(pBaseTexture);
1153 } else {
1154 TRACE("Surface is standalone, no need to dirty the container\n");
1158 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1161 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1162 GLint prev_store;
1163 GLint prev_rasterpos[4];
1164 GLint skipBytes = 0;
1165 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1166 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1167 IWineD3DSwapChainImpl *swapchain;
1169 /* Activate the correct context for the render target */
1170 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1171 ENTER_GL();
1173 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1174 if(!swapchain) {
1175 /* Primary offscreen render target */
1176 TRACE("Offscreen render target\n");
1177 glDrawBuffer(myDevice->offscreenBuffer);
1178 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1179 } else {
1180 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1181 TRACE("Unlocking %#x buffer\n", buffer);
1182 glDrawBuffer(buffer);
1183 checkGLcall("glDrawBuffer");
1185 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1188 glFlush();
1189 vcheckGLcall("glFlush");
1190 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1191 vcheckGLcall("glIntegerv");
1192 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1193 vcheckGLcall("glIntegerv");
1194 glPixelZoom(1.0, -1.0);
1195 vcheckGLcall("glPixelZoom");
1197 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1198 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1199 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1201 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1202 vcheckGLcall("glRasterPos2f");
1204 /* Some drivers(radeon dri, others?) don't like exceptions during
1205 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1206 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1207 * catch to put the dib section in InSync mode, which leads to a crash
1208 * and a blocked x server on my radeon card.
1210 * The following lines read the dib section so it is put in InSync mode
1211 * before glDrawPixels is called and the crash is prevented. There won't
1212 * be any interfering gdi accesses, because UnlockRect is called from
1213 * ReleaseDC, and the app won't use the dc any more afterwards.
1215 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1216 volatile BYTE read;
1217 read = This->resource.allocatedMemory[0];
1220 if(This->Flags & SFLAG_PBO) {
1221 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1222 checkGLcall("glBindBufferARB");
1225 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1226 if(This->Flags & SFLAG_LOCKED) {
1227 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1228 (This->lockedRect.bottom - This->lockedRect.top)-1,
1229 fmt, type,
1230 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1231 checkGLcall("glDrawPixels");
1232 } else {
1233 glDrawPixels(This->currentDesc.Width,
1234 This->currentDesc.Height,
1235 fmt, type, mem);
1236 checkGLcall("glDrawPixels");
1239 if(This->Flags & SFLAG_PBO) {
1240 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1241 checkGLcall("glBindBufferARB");
1244 glPixelZoom(1.0,1.0);
1245 vcheckGLcall("glPixelZoom");
1247 glRasterPos3iv(&prev_rasterpos[0]);
1248 vcheckGLcall("glRasterPos3iv");
1250 /* Reset to previous pack row length */
1251 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1252 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1254 if(!swapchain) {
1255 glDrawBuffer(myDevice->offscreenBuffer);
1256 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1257 } else if(swapchain->backBuffer) {
1258 glDrawBuffer(GL_BACK);
1259 checkGLcall("glDrawBuffer(GL_BACK)");
1260 } else {
1261 glDrawBuffer(GL_FRONT);
1262 checkGLcall("glDrawBuffer(GL_FRONT)");
1264 LEAVE_GL();
1266 return;
1269 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1270 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1271 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1272 IWineD3DSwapChainImpl *swapchain = NULL;
1273 BOOL fullsurface;
1275 if (!(This->Flags & SFLAG_LOCKED)) {
1276 WARN("trying to Unlock an unlocked surf@%p\n", This);
1277 return WINED3DERR_INVALIDCALL;
1280 if (This->Flags & SFLAG_PBO) {
1281 TRACE("Freeing PBO memory\n");
1282 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1283 ENTER_GL();
1284 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1285 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1286 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1287 checkGLcall("glUnmapBufferARB");
1288 LEAVE_GL();
1289 This->resource.allocatedMemory = NULL;
1292 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1294 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1295 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1296 goto unlock_end;
1299 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1300 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1301 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1303 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1304 static BOOL warned = FALSE;
1305 if(!warned) {
1306 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1307 warned = TRUE;
1309 goto unlock_end;
1312 if(This->dirtyRect.left == 0 &&
1313 This->dirtyRect.top == 0 &&
1314 This->dirtyRect.right == This->currentDesc.Width &&
1315 This->dirtyRect.bottom == This->currentDesc.Height) {
1316 fullsurface = TRUE;
1317 } else {
1318 /* TODO: Proper partial rectangle tracking */
1319 fullsurface = FALSE;
1320 This->Flags |= SFLAG_INSYSMEM;
1323 switch(wined3d_settings.rendertargetlock_mode) {
1324 case RTL_READTEX:
1325 case RTL_TEXTEX:
1326 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1327 ENTER_GL();
1328 if (This->glDescription.textureName == 0) {
1329 glGenTextures(1, &This->glDescription.textureName);
1330 checkGLcall("glGenTextures");
1332 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1333 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1334 LEAVE_GL();
1335 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1336 /* drop through */
1338 case RTL_AUTO:
1339 case RTL_READDRAW:
1340 case RTL_TEXDRAW:
1341 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1342 break;
1345 if(!fullsurface) {
1346 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1347 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1348 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1349 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1350 * not fully up to date because only a subrectangle was read in LockRect.
1352 This->Flags &= ~SFLAG_INSYSMEM;
1353 This->Flags |= SFLAG_INDRAWABLE;
1356 This->dirtyRect.left = This->currentDesc.Width;
1357 This->dirtyRect.top = This->currentDesc.Height;
1358 This->dirtyRect.right = 0;
1359 This->dirtyRect.bottom = 0;
1360 } else if(iface == myDevice->stencilBufferTarget) {
1361 FIXME("Depth Stencil buffer locking is not implemented\n");
1362 } else {
1363 /* The rest should be a normal texture */
1364 IWineD3DBaseTextureImpl *impl;
1365 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1366 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1367 * states need resetting
1369 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1370 if(impl->baseTexture.bindCount) {
1371 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1373 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1377 unlock_end:
1378 This->Flags &= ~SFLAG_LOCKED;
1379 memset(&This->lockedRect, 0, sizeof(RECT));
1380 return WINED3D_OK;
1383 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1384 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1385 WINED3DLOCKED_RECT lock;
1386 HRESULT hr;
1387 RGBQUAD col[256];
1389 TRACE("(%p)->(%p)\n",This,pHDC);
1391 if(This->Flags & SFLAG_USERPTR) {
1392 ERR("Not supported on surfaces with an application-provided surfaces\n");
1393 return WINEDDERR_NODC;
1396 /* Give more detailed info for ddraw */
1397 if (This->Flags & SFLAG_DCINUSE)
1398 return WINEDDERR_DCALREADYCREATED;
1400 /* Can't GetDC if the surface is locked */
1401 if (This->Flags & SFLAG_LOCKED)
1402 return WINED3DERR_INVALIDCALL;
1404 /* According to Direct3D9 docs, only these formats are supported */
1405 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1406 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1407 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1408 This->resource.format != WINED3DFMT_R8G8B8 &&
1409 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1412 memset(&lock, 0, sizeof(lock)); /* To be sure */
1414 /* Create a DIB section if there isn't a hdc yet */
1415 if(!This->hDC) {
1416 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1417 if(This->Flags & SFLAG_CLIENT) {
1418 IWineD3DSurface_PreLoad(iface);
1421 /* Use the dib section from now on if we are not using a PBO */
1422 if(!(This->Flags & SFLAG_PBO))
1423 This->resource.allocatedMemory = This->dib.bitmap_data;
1426 /* Lock the surface */
1427 hr = IWineD3DSurface_LockRect(iface,
1428 &lock,
1429 NULL,
1432 if(This->Flags & SFLAG_PBO) {
1433 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1434 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1437 if(FAILED(hr)) {
1438 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1439 /* keep the dib section */
1440 return hr;
1443 if(This->resource.format == WINED3DFMT_P8 ||
1444 This->resource.format == WINED3DFMT_A8P8) {
1445 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1446 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1447 unsigned int n;
1448 PALETTEENTRY *pal = NULL;
1450 if(This->palette) {
1451 pal = This->palette->palents;
1452 } else {
1453 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1454 if (dds_primary && dds_primary->palette)
1455 pal = dds_primary->palette->palents;
1458 if (pal) {
1459 for (n=0; n<256; n++) {
1460 col[n].rgbRed = pal[n].peRed;
1461 col[n].rgbGreen = pal[n].peGreen;
1462 col[n].rgbBlue = pal[n].peBlue;
1463 col[n].rgbReserved = 0;
1465 SetDIBColorTable(This->hDC, 0, 256, col);
1469 *pHDC = This->hDC;
1470 TRACE("returning %p\n",*pHDC);
1471 This->Flags |= SFLAG_DCINUSE;
1473 return WINED3D_OK;
1476 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1477 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1479 TRACE("(%p)->(%p)\n",This,hDC);
1481 if (!(This->Flags & SFLAG_DCINUSE))
1482 return WINED3DERR_INVALIDCALL;
1484 if (This->hDC !=hDC) {
1485 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1486 return WINED3DERR_INVALIDCALL;
1489 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1490 /* Copy the contents of the DIB over to the PBO */
1491 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1494 /* we locked first, so unlock now */
1495 IWineD3DSurface_UnlockRect(iface);
1497 This->Flags &= ~SFLAG_DCINUSE;
1499 return WINED3D_OK;
1502 /* ******************************************************
1503 IWineD3DSurface Internal (No mapping to directx api) parts follow
1504 ****************************************************** */
1506 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) {
1507 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1508 const GlPixelFormatDesc *glDesc;
1509 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1510 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1512 /* Default values: From the surface */
1513 *format = glDesc->glFormat;
1514 *type = glDesc->glType;
1515 *convert = NO_CONVERSION;
1516 *target_bpp = This->bytesPerPixel;
1518 if(srgb_mode) {
1519 *internal = glDesc->glGammaInternal;
1520 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1521 *internal = glDesc->rtInternal;
1522 } else {
1523 *internal = glDesc->glInternal;
1526 /* Ok, now look if we have to do any conversion */
1527 switch(This->resource.format) {
1528 case WINED3DFMT_P8:
1529 /* ****************
1530 Paletted Texture
1531 **************** */
1533 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1534 * of the two is available make sure texturing is requested as neither of the two works in
1535 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1536 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1537 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1538 * conflicts with this.
1540 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1541 *format = GL_RGBA;
1542 *internal = GL_RGBA;
1543 *type = GL_UNSIGNED_BYTE;
1544 *target_bpp = 4;
1545 if(colorkey_active) {
1546 *convert = CONVERT_PALETTED_CK;
1547 } else {
1548 *convert = CONVERT_PALETTED;
1551 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1552 *format = GL_ALPHA;
1553 *internal = GL_RGBA;
1554 *type = GL_UNSIGNED_BYTE;
1555 *target_bpp = 1;
1558 break;
1560 case WINED3DFMT_R3G3B2:
1561 /* **********************
1562 GL_UNSIGNED_BYTE_3_3_2
1563 ********************** */
1564 if (colorkey_active) {
1565 /* This texture format will never be used.. So do not care about color keying
1566 up until the point in time it will be needed :-) */
1567 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1569 break;
1571 case WINED3DFMT_R5G6B5:
1572 if (colorkey_active) {
1573 *convert = CONVERT_CK_565;
1574 *format = GL_RGBA;
1575 *internal = GL_RGBA;
1576 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1578 break;
1580 case WINED3DFMT_X1R5G5B5:
1581 if (colorkey_active) {
1582 *convert = CONVERT_CK_5551;
1583 *format = GL_BGRA;
1584 *internal = GL_RGBA;
1585 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1587 break;
1589 case WINED3DFMT_R8G8B8:
1590 if (colorkey_active) {
1591 *convert = CONVERT_CK_RGB24;
1592 *format = GL_RGBA;
1593 *internal = GL_RGBA;
1594 *type = GL_UNSIGNED_INT_8_8_8_8;
1595 *target_bpp = 4;
1597 break;
1599 case WINED3DFMT_X8R8G8B8:
1600 if (colorkey_active) {
1601 *convert = CONVERT_RGB32_888;
1602 *format = GL_RGBA;
1603 *internal = GL_RGBA;
1604 *type = GL_UNSIGNED_INT_8_8_8_8;
1606 break;
1608 case WINED3DFMT_V8U8:
1609 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1610 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1611 *format = GL_DUDV_ATI;
1612 *internal = GL_DU8DV8_ATI;
1613 *type = GL_BYTE;
1614 /* No conversion - Just change the gl type */
1615 break;
1617 *convert = CONVERT_V8U8;
1618 *format = GL_BGR;
1619 *internal = GL_RGB8;
1620 *type = GL_UNSIGNED_BYTE;
1621 *target_bpp = 3;
1622 break;
1624 case WINED3DFMT_L6V5U5:
1625 *convert = CONVERT_L6V5U5;
1626 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1627 *target_bpp = 3;
1628 /* Use format and types from table */
1629 } else {
1630 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1631 *target_bpp = 2;
1632 *format = GL_RGB;
1633 *internal = GL_RGB5;
1634 *type = GL_UNSIGNED_SHORT_5_6_5;
1636 break;
1638 case WINED3DFMT_X8L8V8U8:
1639 *convert = CONVERT_X8L8V8U8;
1640 *target_bpp = 4;
1641 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1642 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1643 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1644 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1645 * the needed type and format parameter, so the internal format contains a
1646 * 4th component, which is returned as alpha
1648 } else {
1649 /* Not supported by GL_ATI_envmap_bumpmap */
1650 *format = GL_BGRA;
1651 *internal = GL_RGB8;
1652 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1654 break;
1656 case WINED3DFMT_Q8W8V8U8:
1657 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1658 *convert = CONVERT_Q8W8V8U8;
1659 *format = GL_BGRA;
1660 *internal = GL_RGBA8;
1661 *type = GL_UNSIGNED_BYTE;
1662 *target_bpp = 4;
1663 /* Not supported by GL_ATI_envmap_bumpmap */
1664 break;
1666 case WINED3DFMT_V16U16:
1667 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1668 *convert = CONVERT_V16U16;
1669 *format = GL_BGR;
1670 *internal = GL_RGB16_EXT;
1671 *type = GL_UNSIGNED_SHORT;
1672 *target_bpp = 6;
1673 /* What should I do here about GL_ATI_envmap_bumpmap?
1674 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1676 break;
1678 case WINED3DFMT_A4L4:
1679 /* A4L4 exists as an internal gl format, but for some reason there is not
1680 * format+type combination to load it. Thus convert it to A8L8, then load it
1681 * with A4L4 internal, but A8L8 format+type
1683 *convert = CONVERT_A4L4;
1684 *format = GL_LUMINANCE_ALPHA;
1685 *internal = GL_LUMINANCE4_ALPHA4;
1686 *type = GL_UNSIGNED_BYTE;
1687 *target_bpp = 2;
1688 break;
1690 case WINED3DFMT_R32F:
1691 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1692 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1693 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1694 * 1.0 instead.
1696 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1698 *convert = CONVERT_R32F;
1699 *format = GL_RGB;
1700 *internal = GL_RGB32F_ARB;
1701 *type = GL_FLOAT;
1702 *target_bpp = 12;
1703 break;
1705 case WINED3DFMT_R16F:
1706 /* Similar to R32F */
1707 *convert = CONVERT_R16F;
1708 *format = GL_RGB;
1709 *internal = GL_RGB16F_ARB;
1710 *type = GL_HALF_FLOAT_ARB;
1711 *target_bpp = 6;
1712 break;
1714 case WINED3DFMT_G16R16:
1715 *convert = CONVERT_G16R16;
1716 *format = GL_RGB;
1717 *internal = GL_RGB16_EXT;
1718 *type = GL_UNSIGNED_SHORT;
1719 *target_bpp = 6;
1720 break;
1722 default:
1723 break;
1726 return WINED3D_OK;
1729 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1730 BYTE *source, *dest;
1731 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1733 switch (convert) {
1734 case NO_CONVERSION:
1736 memcpy(dst, src, pitch * height);
1737 break;
1739 case CONVERT_PALETTED:
1740 case CONVERT_PALETTED_CK:
1742 IWineD3DPaletteImpl* pal = This->palette;
1743 BYTE table[256][4];
1744 unsigned int x, y;
1746 if( pal == NULL) {
1747 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1750 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1752 for (y = 0; y < height; y++)
1754 source = src + pitch * y;
1755 dest = dst + outpitch * y;
1756 /* This is an 1 bpp format, using the width here is fine */
1757 for (x = 0; x < width; x++) {
1758 BYTE color = *source++;
1759 *dest++ = table[color][0];
1760 *dest++ = table[color][1];
1761 *dest++ = table[color][2];
1762 *dest++ = table[color][3];
1766 break;
1768 case CONVERT_CK_565:
1770 /* Converting the 565 format in 5551 packed to emulate color-keying.
1772 Note : in all these conversion, it would be best to average the averaging
1773 pixels to get the color of the pixel that will be color-keyed to
1774 prevent 'color bleeding'. This will be done later on if ever it is
1775 too visible.
1777 Note2: Nvidia documents say that their driver does not support alpha + color keying
1778 on the same surface and disables color keying in such a case
1780 unsigned int x, y;
1781 WORD *Source;
1782 WORD *Dest;
1784 TRACE("Color keyed 565\n");
1786 for (y = 0; y < height; y++) {
1787 Source = (WORD *) (src + y * pitch);
1788 Dest = (WORD *) (dst + y * outpitch);
1789 for (x = 0; x < width; x++ ) {
1790 WORD color = *Source++;
1791 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1792 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1793 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1794 *Dest |= 0x0001;
1796 Dest++;
1800 break;
1802 case CONVERT_CK_5551:
1804 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1805 unsigned int x, y;
1806 WORD *Source;
1807 WORD *Dest;
1808 TRACE("Color keyed 5551\n");
1809 for (y = 0; y < height; y++) {
1810 Source = (WORD *) (src + y * pitch);
1811 Dest = (WORD *) (dst + y * outpitch);
1812 for (x = 0; x < width; x++ ) {
1813 WORD color = *Source++;
1814 *Dest = color;
1815 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1816 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1817 *Dest |= (1 << 15);
1819 else {
1820 *Dest &= ~(1 << 15);
1822 Dest++;
1826 break;
1828 case CONVERT_RGB32_888:
1830 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1831 unsigned int x, y;
1832 for (y = 0; y < height; y++)
1834 source = src + pitch * y;
1835 dest = dst + outpitch * y;
1836 for (x = 0; x < width; x++) {
1837 DWORD color = 0xffffff & *(DWORD*)source;
1838 DWORD dstcolor = color << 8;
1839 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1840 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1841 dstcolor |= 0xff;
1843 *(DWORD*)dest = dstcolor;
1844 source += 4;
1845 dest += 4;
1849 break;
1851 case CONVERT_V8U8:
1853 unsigned int x, y;
1854 short *Source;
1855 unsigned char *Dest;
1856 for(y = 0; y < height; y++) {
1857 Source = (short *) (src + y * pitch);
1858 Dest = dst + y * outpitch;
1859 for (x = 0; x < width; x++ ) {
1860 long color = (*Source++);
1861 /* B */ Dest[0] = 0xff;
1862 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1863 /* R */ Dest[2] = (color) + 128; /* U */
1864 Dest += 3;
1867 break;
1870 case CONVERT_V16U16:
1872 unsigned int x, y;
1873 DWORD *Source;
1874 unsigned short *Dest;
1875 for(y = 0; y < height; y++) {
1876 Source = (DWORD *) (src + y * pitch);
1877 Dest = (unsigned short *) (dst + y * outpitch);
1878 for (x = 0; x < width; x++ ) {
1879 DWORD color = (*Source++);
1880 /* B */ Dest[0] = 0xffff;
1881 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1882 /* R */ Dest[2] = (color ) + 32768; /* U */
1883 Dest += 3;
1886 break;
1889 case CONVERT_Q8W8V8U8:
1891 unsigned int x, y;
1892 DWORD *Source;
1893 unsigned char *Dest;
1894 for(y = 0; y < height; y++) {
1895 Source = (DWORD *) (src + y * pitch);
1896 Dest = dst + y * outpitch;
1897 for (x = 0; x < width; x++ ) {
1898 long color = (*Source++);
1899 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1900 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1901 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1902 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1903 Dest += 4;
1906 break;
1909 case CONVERT_L6V5U5:
1911 unsigned int x, y;
1912 WORD *Source;
1913 unsigned char *Dest;
1915 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1916 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1917 * fixed function and shaders without further conversion once the surface is
1918 * loaded
1920 for(y = 0; y < height; y++) {
1921 Source = (WORD *) (src + y * pitch);
1922 Dest = dst + y * outpitch;
1923 for (x = 0; x < width; x++ ) {
1924 short color = (*Source++);
1925 unsigned char l = ((color >> 10) & 0xfc);
1926 char v = ((color >> 5) & 0x3e);
1927 char u = ((color ) & 0x1f);
1929 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1930 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1931 * shift. GL reads a signed value and converts it into an unsigned value.
1933 /* M */ Dest[2] = l << 1;
1935 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1936 * from 5 bit values to 8 bit values.
1938 /* V */ Dest[1] = v << 3;
1939 /* U */ Dest[0] = u << 3;
1940 Dest += 3;
1943 } else {
1944 for(y = 0; y < height; y++) {
1945 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1946 Source = (WORD *) (src + y * pitch);
1947 for (x = 0; x < width; x++ ) {
1948 short color = (*Source++);
1949 unsigned char l = ((color >> 10) & 0xfc);
1950 short v = ((color >> 5) & 0x3e);
1951 short u = ((color ) & 0x1f);
1952 short v_conv = v + 16;
1953 short u_conv = u + 16;
1955 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1956 Dest_s += 1;
1960 break;
1963 case CONVERT_X8L8V8U8:
1965 unsigned int x, y;
1966 DWORD *Source;
1967 unsigned char *Dest;
1969 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1970 /* This implementation works with the fixed function pipeline and shaders
1971 * without further modification after converting the surface.
1973 for(y = 0; y < height; y++) {
1974 Source = (DWORD *) (src + y * pitch);
1975 Dest = dst + y * outpitch;
1976 for (x = 0; x < width; x++ ) {
1977 long color = (*Source++);
1978 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1979 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1980 /* U */ Dest[0] = (color & 0xff); /* U */
1981 /* I */ Dest[3] = 255; /* X */
1982 Dest += 4;
1985 } else {
1986 /* Doesn't work correctly with the fixed function pipeline, but can work in
1987 * shaders if the shader is adjusted. (There's no use for this format in gl's
1988 * standard fixed function pipeline anyway).
1990 for(y = 0; y < height; y++) {
1991 Source = (DWORD *) (src + y * pitch);
1992 Dest = dst + y * outpitch;
1993 for (x = 0; x < width; x++ ) {
1994 long color = (*Source++);
1995 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1996 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1997 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1998 Dest += 4;
2002 break;
2005 case CONVERT_A4L4:
2007 unsigned int x, y;
2008 unsigned char *Source;
2009 unsigned char *Dest;
2010 for(y = 0; y < height; y++) {
2011 Source = src + y * pitch;
2012 Dest = dst + y * outpitch;
2013 for (x = 0; x < width; x++ ) {
2014 unsigned char color = (*Source++);
2015 /* A */ Dest[1] = (color & 0xf0) << 0;
2016 /* L */ Dest[0] = (color & 0x0f) << 4;
2017 Dest += 2;
2020 break;
2023 case CONVERT_R32F:
2025 unsigned int x, y;
2026 float *Source;
2027 float *Dest;
2028 for(y = 0; y < height; y++) {
2029 Source = (float *) (src + y * pitch);
2030 Dest = (float *) (dst + y * outpitch);
2031 for (x = 0; x < width; x++ ) {
2032 float color = (*Source++);
2033 Dest[0] = color;
2034 Dest[1] = 1.0;
2035 Dest[2] = 1.0;
2036 Dest += 3;
2039 break;
2042 case CONVERT_R16F:
2044 unsigned int x, y;
2045 WORD *Source;
2046 WORD *Dest;
2047 WORD one = 0x3c00;
2048 for(y = 0; y < height; y++) {
2049 Source = (WORD *) (src + y * pitch);
2050 Dest = (WORD *) (dst + y * outpitch);
2051 for (x = 0; x < width; x++ ) {
2052 WORD color = (*Source++);
2053 Dest[0] = color;
2054 Dest[1] = one;
2055 Dest[2] = one;
2056 Dest += 3;
2059 break;
2062 case CONVERT_G16R16:
2064 unsigned int x, y;
2065 WORD *Source;
2066 WORD *Dest;
2068 for(y = 0; y < height; y++) {
2069 Source = (WORD *) (src + y * pitch);
2070 Dest = (WORD *) (dst + y * outpitch);
2071 for (x = 0; x < width; x++ ) {
2072 WORD green = (*Source++);
2073 WORD red = (*Source++);
2074 Dest[0] = green;
2075 Dest[1] = red;
2076 Dest[2] = 0xffff;
2077 Dest += 3;
2080 break;
2083 default:
2084 ERR("Unsupported conversation type %d\n", convert);
2086 return WINED3D_OK;
2089 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2090 IWineD3DPaletteImpl* pal = This->palette;
2091 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2092 BOOL index_in_alpha = FALSE;
2093 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2094 int i;
2096 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2097 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2098 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2099 * duplicate entries. Store the color key in the unused alpha component to speed the
2100 * download up and to make conversion unneeded. */
2101 index_in_alpha = primary_render_target_is_p8(device);
2103 if (pal == NULL) {
2104 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2105 if(dxVersion <= 7) {
2106 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2107 if(index_in_alpha) {
2108 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2109 there's no palette at this time. */
2110 for (i = 0; i < 256; i++) table[i][3] = i;
2112 } else {
2113 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2114 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2115 capability flag is present (wine does advertise this capability) */
2116 for (i = 0; i < 256; i++) {
2117 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2118 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2119 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2120 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2123 } else {
2124 TRACE("Using surface palette %p\n", pal);
2125 /* Get the surface's palette */
2126 for (i = 0; i < 256; i++) {
2127 table[i][0] = pal->palents[i].peRed;
2128 table[i][1] = pal->palents[i].peGreen;
2129 table[i][2] = pal->palents[i].peBlue;
2131 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2132 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2133 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2134 of pixels that should be masked away is set to 0. */
2135 if(index_in_alpha) {
2136 table[i][3] = i;
2137 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2138 table[i][3] = 0x00;
2139 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2140 table[i][3] = pal->palents[i].peFlags;
2141 } else {
2142 table[i][3] = 0xFF;
2148 const char *fragment_palette_conversion =
2149 "!!ARBfp1.0\n"
2150 "TEMP index;\n"
2151 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2152 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2153 "MAD index.a, index.a, constants.x, constants.y;\n" /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2154 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2155 "END";
2157 /* This function is used in case of 8bit paletted textures to upload the palette.
2158 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2159 extensions like ATI_fragment_shaders is possible.
2161 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2162 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2163 BYTE table[256][4];
2164 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2166 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2168 /* Try to use the paletted texture extension */
2169 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2171 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2172 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2174 else
2176 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2177 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2178 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2180 /* Create the fragment program if we don't have it */
2181 if(!device->paletteConversionShader)
2183 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2184 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2185 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2186 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2187 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2190 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2191 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2193 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2194 glEnable(GL_TEXTURE_1D);
2195 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2197 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2198 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2199 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2200 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2202 /* Switch back to unit 0 in which the 2D texture will be stored. */
2203 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2205 /* Rebind the texture because it isn't bound anymore */
2206 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2210 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2211 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2213 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2214 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2215 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2217 return FALSE;
2220 if(This->palette9) {
2221 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2222 return FALSE;
2224 } else {
2225 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2227 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2228 return TRUE;
2231 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2232 GLboolean oldwrite[4];
2234 /* Some formats have only some color channels, and the others are 1.0.
2235 * since our rendering renders to all channels, and those pixel formats
2236 * are emulated by using a full texture with the other channels set to 1.0
2237 * manually, clear the unused channels.
2239 * This could be done with hacking colorwriteenable to mask the colors,
2240 * but before drawing the buffer would have to be cleared too, so there's
2241 * no gain in that
2243 switch(This->resource.format) {
2244 case WINED3DFMT_R16F:
2245 case WINED3DFMT_R32F:
2246 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2247 /* Do not activate a context, the correct drawable is active already
2248 * though just the read buffer is set, make sure to have the correct draw
2249 * buffer too
2251 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2252 glDisable(GL_SCISSOR_TEST);
2253 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2254 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2255 glClearColor(0.0, 1.0, 1.0, 1.0);
2256 glClear(GL_COLOR_BUFFER_BIT);
2257 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2258 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2259 checkGLcall("Unused channel clear\n");
2260 break;
2262 default: break;
2266 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2267 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2269 if (!(This->Flags & SFLAG_INTEXTURE)) {
2270 TRACE("Reloading because surface is dirty\n");
2271 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2272 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2273 /* Reload: vice versa OR */
2274 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2275 /* Also reload: Color key is active AND the color key has changed */
2276 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2277 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2278 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2279 TRACE("Reloading because of color keying\n");
2280 /* To perform the color key conversion we need a sysmem copy of
2281 * the surface. Make sure we have it
2284 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2285 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2286 /* TODO: This is not necessarily needed with hw palettized texture support */
2287 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2288 } else {
2289 TRACE("surface is already in texture\n");
2290 return WINED3D_OK;
2293 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2294 * These resources are not bound by device size or format restrictions. Because of this,
2295 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2296 * However, these resources can always be created, locked, and copied.
2298 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2300 FIXME("(%p) Operation not supported for scratch textures\n",This);
2301 return WINED3DERR_INVALIDCALL;
2304 This->srgb = srgb_mode;
2305 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2307 #if 0
2309 static unsigned int gen = 0;
2310 char buffer[4096];
2311 ++gen;
2312 if ((gen % 10) == 0) {
2313 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2314 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2317 * debugging crash code
2318 if (gen == 250) {
2319 void** test = NULL;
2320 *test = 0;
2324 #endif
2326 if (!(This->Flags & SFLAG_DONOTFREE)) {
2327 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2328 This->resource.allocatedMemory = NULL;
2329 This->resource.heapMemory = NULL;
2330 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2333 return WINED3D_OK;
2336 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2337 /* TODO: check for locks */
2338 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2339 IWineD3DBaseTexture *baseTexture = NULL;
2340 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2342 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2343 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2344 TRACE("Passing to container\n");
2345 IWineD3DBaseTexture_BindTexture(baseTexture);
2346 IWineD3DBaseTexture_Release(baseTexture);
2347 } else {
2348 TRACE("(%p) : Binding surface\n", This);
2350 if(!device->isInDraw) {
2351 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2353 ENTER_GL();
2354 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2355 LEAVE_GL();
2357 return;
2360 #include <errno.h>
2361 #include <stdio.h>
2362 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2363 FILE* f = NULL;
2364 UINT i, y;
2365 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2366 char *allocatedMemory;
2367 char *textureRow;
2368 IWineD3DSwapChain *swapChain = NULL;
2369 int width, height;
2370 GLuint tmpTexture = 0;
2371 DWORD color;
2372 /*FIXME:
2373 Textures may not be stored in ->allocatedgMemory and a GlTexture
2374 so we should lock the surface before saving a snapshot, or at least check that
2376 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2377 by calling GetTexImage and in compressed form by calling
2378 GetCompressedTexImageARB. Queried compressed images can be saved and
2379 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2380 texture images do not need to be processed by the GL and should
2381 significantly improve texture loading performance relative to uncompressed
2382 images. */
2384 /* Setup the width and height to be the internal texture width and height. */
2385 width = This->pow2Width;
2386 height = This->pow2Height;
2387 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2388 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2390 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2391 /* if were not a real texture then read the back buffer into a real texture */
2392 /* we don't want to interfere with the back buffer so read the data into a temporary
2393 * texture and then save the data out of the temporary texture
2395 GLint prevRead;
2396 ENTER_GL();
2397 TRACE("(%p) Reading render target into texture\n", This);
2398 glEnable(GL_TEXTURE_2D);
2400 glGenTextures(1, &tmpTexture);
2401 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2403 glTexImage2D(GL_TEXTURE_2D,
2405 GL_RGBA,
2406 width,
2407 height,
2408 0/*border*/,
2409 GL_RGBA,
2410 GL_UNSIGNED_INT_8_8_8_8_REV,
2411 NULL);
2413 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2414 vcheckGLcall("glGetIntegerv");
2415 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2416 vcheckGLcall("glReadBuffer");
2417 glCopyTexImage2D(GL_TEXTURE_2D,
2419 GL_RGBA,
2422 width,
2423 height,
2426 checkGLcall("glCopyTexImage2D");
2427 glReadBuffer(prevRead);
2428 LEAVE_GL();
2430 } else { /* bind the real texture, and make sure it up to date */
2431 IWineD3DSurface_PreLoad(iface);
2433 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2434 ENTER_GL();
2435 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2436 glGetTexImage(GL_TEXTURE_2D,
2437 This->glDescription.level,
2438 GL_RGBA,
2439 GL_UNSIGNED_INT_8_8_8_8_REV,
2440 allocatedMemory);
2441 checkGLcall("glTexImage2D");
2442 if (tmpTexture) {
2443 glBindTexture(GL_TEXTURE_2D, 0);
2444 glDeleteTextures(1, &tmpTexture);
2446 LEAVE_GL();
2448 f = fopen(filename, "w+");
2449 if (NULL == f) {
2450 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2451 return WINED3DERR_INVALIDCALL;
2453 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2454 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2455 /* TGA header */
2456 fputc(0,f);
2457 fputc(0,f);
2458 fputc(2,f);
2459 fputc(0,f);
2460 fputc(0,f);
2461 fputc(0,f);
2462 fputc(0,f);
2463 fputc(0,f);
2464 fputc(0,f);
2465 fputc(0,f);
2466 fputc(0,f);
2467 fputc(0,f);
2468 /* short width*/
2469 fwrite(&width,2,1,f);
2470 /* short height */
2471 fwrite(&height,2,1,f);
2472 /* format rgba */
2473 fputc(0x20,f);
2474 fputc(0x28,f);
2475 /* raw data */
2476 /* 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 */
2477 if(swapChain)
2478 textureRow = allocatedMemory + (width * (height - 1) *4);
2479 else
2480 textureRow = allocatedMemory;
2481 for (y = 0 ; y < height; y++) {
2482 for (i = 0; i < width; i++) {
2483 color = *((DWORD*)textureRow);
2484 fputc((color >> 16) & 0xFF, f); /* B */
2485 fputc((color >> 8) & 0xFF, f); /* G */
2486 fputc((color >> 0) & 0xFF, f); /* R */
2487 fputc((color >> 24) & 0xFF, f); /* A */
2488 textureRow += 4;
2490 /* take two rows of the pointer to the texture memory */
2491 if(swapChain)
2492 (textureRow-= width << 3);
2495 TRACE("Closing file\n");
2496 fclose(f);
2498 if(swapChain) {
2499 IWineD3DSwapChain_Release(swapChain);
2501 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2502 return WINED3D_OK;
2506 * Slightly inefficient way to handle multiple dirty rects but it works :)
2508 HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2509 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2510 IWineD3DBaseTexture *baseTexture = NULL;
2512 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2513 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2515 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2516 if (NULL != pDirtyRect) {
2517 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2518 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2519 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2520 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2521 } else {
2522 This->dirtyRect.left = 0;
2523 This->dirtyRect.top = 0;
2524 This->dirtyRect.right = This->currentDesc.Width;
2525 This->dirtyRect.bottom = This->currentDesc.Height;
2527 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2528 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2529 /* if the container is a basetexture then mark it dirty. */
2530 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2531 TRACE("Passing to container\n");
2532 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2533 IWineD3DBaseTexture_Release(baseTexture);
2535 return WINED3D_OK;
2538 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2539 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2540 HRESULT hr;
2541 const GlPixelFormatDesc *glDesc;
2542 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2544 TRACE("(%p) : Calling base function first\n", This);
2545 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2546 if(SUCCEEDED(hr)) {
2547 /* Setup some glformat defaults */
2548 This->glDescription.glFormat = glDesc->glFormat;
2549 This->glDescription.glFormatInternal = glDesc->glInternal;
2550 This->glDescription.glType = glDesc->glType;
2552 This->Flags &= ~SFLAG_ALLOCATED;
2553 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2554 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2556 return hr;
2559 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2560 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2562 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2563 WARN("Surface is locked or the HDC is in use\n");
2564 return WINED3DERR_INVALIDCALL;
2567 if(Mem && Mem != This->resource.allocatedMemory) {
2568 void *release = NULL;
2570 /* Do I have to copy the old surface content? */
2571 if(This->Flags & SFLAG_DIBSECTION) {
2572 /* Release the DC. No need to hold the critical section for the update
2573 * Thread because this thread runs only on front buffers, but this method
2574 * fails for render targets in the check above.
2576 SelectObject(This->hDC, This->dib.holdbitmap);
2577 DeleteDC(This->hDC);
2578 /* Release the DIB section */
2579 DeleteObject(This->dib.DIBsection);
2580 This->dib.bitmap_data = NULL;
2581 This->resource.allocatedMemory = NULL;
2582 This->hDC = NULL;
2583 This->Flags &= ~SFLAG_DIBSECTION;
2584 } else if(!(This->Flags & SFLAG_USERPTR)) {
2585 release = This->resource.heapMemory;
2586 This->resource.heapMemory = NULL;
2588 This->resource.allocatedMemory = Mem;
2589 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2591 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2592 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2594 /* For client textures opengl has to be notified */
2595 if(This->Flags & SFLAG_CLIENT) {
2596 This->Flags &= ~SFLAG_ALLOCATED;
2597 IWineD3DSurface_PreLoad(iface);
2598 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2601 /* Now free the old memory if any */
2602 HeapFree(GetProcessHeap(), 0, release);
2603 } else if(This->Flags & SFLAG_USERPTR) {
2604 /* LockRect and GetDC will re-create the dib section and allocated memory */
2605 This->resource.allocatedMemory = NULL;
2606 /* HeapMemory should be NULL already */
2607 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2608 This->Flags &= ~SFLAG_USERPTR;
2610 if(This->Flags & SFLAG_CLIENT) {
2611 This->Flags &= ~SFLAG_ALLOCATED;
2612 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2613 IWineD3DSurface_PreLoad(iface);
2616 return WINED3D_OK;
2619 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2620 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2621 IWineD3DSwapChainImpl *swapchain = NULL;
2622 HRESULT hr;
2623 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2625 /* Flipping is only supported on RenderTargets */
2626 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2628 if(override) {
2629 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2630 * FIXME("(%p) Target override is not supported by now\n", This);
2631 * Additionally, it isn't really possible to support triple-buffering
2632 * properly on opengl at all
2636 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2637 if(!swapchain) {
2638 ERR("Flipped surface is not on a swapchain\n");
2639 return WINEDDERR_NOTFLIPPABLE;
2642 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2643 * and only d3d8 and d3d9 apps specify the presentation interval
2645 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2646 /* Most common case first to avoid wasting time on all the other cases */
2647 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2648 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2649 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2650 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2651 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2652 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2653 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2654 } else {
2655 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2658 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2659 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2660 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2661 return hr;
2664 /* Does a direct frame buffer -> texture copy. Stretching is done
2665 * with single pixel copy calls
2667 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2668 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2669 float xrel, yrel;
2670 UINT row;
2671 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2674 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2675 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2676 ENTER_GL();
2678 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2679 glEnable(This->glDescription.target);
2680 checkGLcall("glEnable(This->glDescription.target)");
2682 /* Bind the target texture */
2683 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2684 checkGLcall("glBindTexture");
2685 if(!swapchain) {
2686 TRACE("Reading from an offscreen target\n");
2687 upsidedown = !upsidedown;
2688 glReadBuffer(myDevice->offscreenBuffer);
2689 } else {
2690 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2691 glReadBuffer(buffer);
2693 checkGLcall("glReadBuffer");
2695 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2696 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2698 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2699 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2701 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2702 ERR("Texture filtering not supported in direct blit\n");
2704 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2705 ERR("Texture filtering not supported in direct blit\n");
2708 if(upsidedown &&
2709 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2710 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2711 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2713 glCopyTexSubImage2D(This->glDescription.target,
2714 This->glDescription.level,
2715 drect->x1, drect->y1, /* xoffset, yoffset */
2716 srect->x1, Src->currentDesc.Height - srect->y2,
2717 drect->x2 - drect->x1, drect->y2 - drect->y1);
2718 } else {
2719 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2720 /* I have to process this row by row to swap the image,
2721 * otherwise it would be upside down, so stretching in y direction
2722 * doesn't cost extra time
2724 * However, stretching in x direction can be avoided if not necessary
2726 for(row = drect->y1; row < drect->y2; row++) {
2727 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2728 /* Well, that stuff works, but it's very slow.
2729 * find a better way instead
2731 UINT col;
2733 for(col = drect->x1; col < drect->x2; col++) {
2734 glCopyTexSubImage2D(This->glDescription.target,
2735 This->glDescription.level,
2736 drect->x1 + col, row, /* xoffset, yoffset */
2737 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2738 1, 1);
2740 } else {
2741 glCopyTexSubImage2D(This->glDescription.target,
2742 This->glDescription.level,
2743 drect->x1, row, /* xoffset, yoffset */
2744 srect->x1, yoffset - (int) (row * yrel),
2745 drect->x2-drect->x1, 1);
2749 vcheckGLcall("glCopyTexSubImage2D");
2751 /* Leave the opengl state valid for blitting */
2752 glDisable(This->glDescription.target);
2753 checkGLcall("glDisable(This->glDescription.target)");
2755 LEAVE_GL();
2758 /* Uses the hardware to stretch and flip the image */
2759 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2760 GLuint src, backup = 0;
2761 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2762 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2763 float left, right, top, bottom; /* Texture coordinates */
2764 UINT fbwidth = Src->currentDesc.Width;
2765 UINT fbheight = Src->currentDesc.Height;
2766 GLenum drawBuffer = GL_BACK;
2767 GLenum texture_target;
2768 BOOL noBackBufferBackup;
2770 TRACE("Using hwstretch blit\n");
2771 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2772 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2773 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2775 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2776 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2777 /* Get it a description */
2778 IWineD3DSurface_PreLoad(SrcSurface);
2780 ENTER_GL();
2782 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2783 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2785 if(myDevice->activeContext->aux_buffers >= 2) {
2786 /* Got more than one aux buffer? Use the 2nd aux buffer */
2787 drawBuffer = GL_AUX1;
2788 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2789 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2790 drawBuffer = GL_AUX0;
2793 if(noBackBufferBackup) {
2794 glGenTextures(1, &backup);
2795 checkGLcall("glGenTextures\n");
2796 glBindTexture(GL_TEXTURE_2D, backup);
2797 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2798 texture_target = GL_TEXTURE_2D;
2799 } else {
2800 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2801 * we are reading from the back buffer, the backup can be used as source texture
2803 texture_target = Src->glDescription.target;
2804 glBindTexture(texture_target, Src->glDescription.textureName);
2805 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2806 glEnable(texture_target);
2807 checkGLcall("glEnable(texture_target)");
2809 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2810 Src->Flags &= ~SFLAG_INTEXTURE;
2813 if(swapchain) {
2814 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2815 } else {
2816 TRACE("Reading from an offscreen target\n");
2817 upsidedown = !upsidedown;
2818 glReadBuffer(myDevice->offscreenBuffer);
2821 /* TODO: Only back up the part that will be overwritten */
2822 glCopyTexSubImage2D(texture_target, 0,
2823 0, 0 /* read offsets */,
2824 0, 0,
2825 fbwidth,
2826 fbheight);
2828 checkGLcall("glCopyTexSubImage2D");
2830 /* No issue with overriding these - the sampler is dirty due to blit usage */
2831 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2832 magLookup[Filter - WINED3DTEXF_NONE]);
2833 checkGLcall("glTexParameteri");
2834 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2835 minMipLookup[Filter][WINED3DTEXF_NONE]);
2836 checkGLcall("glTexParameteri");
2838 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2839 src = backup ? backup : Src->glDescription.textureName;
2840 } else {
2841 glReadBuffer(GL_FRONT);
2842 checkGLcall("glReadBuffer(GL_FRONT)");
2844 glGenTextures(1, &src);
2845 checkGLcall("glGenTextures(1, &src)");
2846 glBindTexture(GL_TEXTURE_2D, src);
2847 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2849 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2850 * out for power of 2 sizes
2852 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2853 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2854 checkGLcall("glTexImage2D");
2855 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2856 0, 0 /* read offsets */,
2857 0, 0,
2858 fbwidth,
2859 fbheight);
2861 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2862 checkGLcall("glTexParameteri");
2863 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2864 checkGLcall("glTexParameteri");
2866 glReadBuffer(GL_BACK);
2867 checkGLcall("glReadBuffer(GL_BACK)");
2869 if(texture_target != GL_TEXTURE_2D) {
2870 glDisable(texture_target);
2871 glEnable(GL_TEXTURE_2D);
2872 texture_target = GL_TEXTURE_2D;
2875 checkGLcall("glEnd and previous");
2877 left = srect->x1;
2878 right = srect->x2;
2880 if(upsidedown) {
2881 top = Src->currentDesc.Height - srect->y1;
2882 bottom = Src->currentDesc.Height - srect->y2;
2883 } else {
2884 top = Src->currentDesc.Height - srect->y2;
2885 bottom = Src->currentDesc.Height - srect->y1;
2888 if(Src->Flags & SFLAG_NORMCOORD) {
2889 left /= Src->pow2Width;
2890 right /= Src->pow2Width;
2891 top /= Src->pow2Height;
2892 bottom /= Src->pow2Height;
2895 /* draw the source texture stretched and upside down. The correct surface is bound already */
2896 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2897 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2899 glDrawBuffer(drawBuffer);
2900 glReadBuffer(drawBuffer);
2902 glBegin(GL_QUADS);
2903 /* bottom left */
2904 glTexCoord2f(left, bottom);
2905 glVertex2i(0, fbheight);
2907 /* top left */
2908 glTexCoord2f(left, top);
2909 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2911 /* top right */
2912 glTexCoord2f(right, top);
2913 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2915 /* bottom right */
2916 glTexCoord2f(right, bottom);
2917 glVertex2i(drect->x2 - drect->x1, fbheight);
2918 glEnd();
2919 checkGLcall("glEnd and previous");
2921 if(texture_target != This->glDescription.target) {
2922 glDisable(texture_target);
2923 glEnable(This->glDescription.target);
2924 texture_target = This->glDescription.target;
2927 /* Now read the stretched and upside down image into the destination texture */
2928 glBindTexture(texture_target, This->glDescription.textureName);
2929 checkGLcall("glBindTexture");
2930 glCopyTexSubImage2D(texture_target,
2932 drect->x1, drect->y1, /* xoffset, yoffset */
2933 0, 0, /* We blitted the image to the origin */
2934 drect->x2 - drect->x1, drect->y2 - drect->y1);
2935 checkGLcall("glCopyTexSubImage2D");
2937 if(drawBuffer == GL_BACK) {
2938 /* Write the back buffer backup back */
2939 if(backup) {
2940 if(texture_target != GL_TEXTURE_2D) {
2941 glDisable(texture_target);
2942 glEnable(GL_TEXTURE_2D);
2943 texture_target = GL_TEXTURE_2D;
2945 glBindTexture(GL_TEXTURE_2D, backup);
2946 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2947 } else {
2948 if(texture_target != Src->glDescription.target) {
2949 glDisable(texture_target);
2950 glEnable(Src->glDescription.target);
2951 texture_target = Src->glDescription.target;
2953 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2954 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2957 glBegin(GL_QUADS);
2958 /* top left */
2959 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2960 glVertex2i(0, 0);
2962 /* bottom left */
2963 glTexCoord2f(0.0, 0.0);
2964 glVertex2i(0, fbheight);
2966 /* bottom right */
2967 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2968 glVertex2i(fbwidth, Src->currentDesc.Height);
2970 /* top right */
2971 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2972 glVertex2i(fbwidth, 0);
2973 glEnd();
2974 } else {
2975 /* Restore the old draw buffer */
2976 glDrawBuffer(GL_BACK);
2978 glDisable(texture_target);
2979 checkGLcall("glDisable(texture_target)");
2981 /* Cleanup */
2982 if(src != Src->glDescription.textureName && src != backup) {
2983 glDeleteTextures(1, &src);
2984 checkGLcall("glDeleteTextures(1, &src)");
2986 if(backup) {
2987 glDeleteTextures(1, &backup);
2988 checkGLcall("glDeleteTextures(1, &backup)");
2991 LEAVE_GL();
2994 /* Not called from the VTable */
2995 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2996 WINED3DRECT rect;
2997 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2998 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2999 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3001 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3003 /* Get the swapchain. One of the surfaces has to be a primary surface */
3004 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3005 WARN("Destination is in sysmem, rejecting gl blt\n");
3006 return WINED3DERR_INVALIDCALL;
3008 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3009 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3010 if(Src) {
3011 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3012 WARN("Src is in sysmem, rejecting gl blt\n");
3013 return WINED3DERR_INVALIDCALL;
3015 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3016 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3019 /* Early sort out of cases where no render target is used */
3020 if(!dstSwapchain && !srcSwapchain &&
3021 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3022 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3023 return WINED3DERR_INVALIDCALL;
3026 /* No destination color keying supported */
3027 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3028 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3029 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3030 return WINED3DERR_INVALIDCALL;
3033 if (DestRect) {
3034 rect.x1 = DestRect->left;
3035 rect.y1 = DestRect->top;
3036 rect.x2 = DestRect->right;
3037 rect.y2 = DestRect->bottom;
3038 } else {
3039 rect.x1 = 0;
3040 rect.y1 = 0;
3041 rect.x2 = This->currentDesc.Width;
3042 rect.y2 = This->currentDesc.Height;
3045 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3046 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3047 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3048 /* Half-life does a Blt from the back buffer to the front buffer,
3049 * Full surface size, no flags... Use present instead
3051 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3054 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3055 while(1)
3057 RECT mySrcRect;
3058 TRACE("Looking if a Present can be done...\n");
3059 /* Source Rectangle must be full surface */
3060 if( SrcRect ) {
3061 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3062 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3063 TRACE("No, Source rectangle doesn't match\n");
3064 break;
3067 mySrcRect.left = 0;
3068 mySrcRect.top = 0;
3069 mySrcRect.right = Src->currentDesc.Width;
3070 mySrcRect.bottom = Src->currentDesc.Height;
3072 /* No stretching may occur */
3073 if(mySrcRect.right != rect.x2 - rect.x1 ||
3074 mySrcRect.bottom != rect.y2 - rect.y1) {
3075 TRACE("No, stretching is done\n");
3076 break;
3079 /* Destination must be full surface or match the clipping rectangle */
3080 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3082 RECT cliprect;
3083 POINT pos[2];
3084 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3085 pos[0].x = rect.x1;
3086 pos[0].y = rect.y1;
3087 pos[1].x = rect.x2;
3088 pos[1].y = rect.y2;
3089 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3090 pos, 2);
3092 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3093 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3095 TRACE("No, dest rectangle doesn't match(clipper)\n");
3096 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3097 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3098 break;
3101 else
3103 if(rect.x1 != 0 || rect.y1 != 0 ||
3104 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3105 TRACE("No, dest rectangle doesn't match(surface size)\n");
3106 break;
3110 TRACE("Yes\n");
3112 /* These flags are unimportant for the flag check, remove them */
3113 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3114 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3116 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3117 * take very long, while a flip is fast.
3118 * This applies to Half-Life, which does such Blts every time it finished
3119 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3120 * menu. This is also used by all apps when they do windowed rendering
3122 * The problem is that flipping is not really the same as copying. After a
3123 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3124 * untouched. Therefore it's necessary to override the swap effect
3125 * and to set it back after the flip.
3127 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3128 * testcases.
3131 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3132 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3134 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3135 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3137 dstSwapchain->presentParms.SwapEffect = orig_swap;
3139 return WINED3D_OK;
3141 break;
3144 TRACE("Unsupported blit between buffers on the same swapchain\n");
3145 return WINED3DERR_INVALIDCALL;
3146 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3147 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3148 return WINED3DERR_INVALIDCALL;
3149 } else if(dstSwapchain && srcSwapchain) {
3150 FIXME("Implement hardware blit between two different swapchains\n");
3151 return WINED3DERR_INVALIDCALL;
3152 } else if(dstSwapchain) {
3153 if(SrcSurface == myDevice->render_targets[0]) {
3154 TRACE("Blit from active render target to a swapchain\n");
3155 /* Handled with regular texture -> swapchain blit */
3157 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3158 FIXME("Implement blit from a swapchain to the active render target\n");
3159 return WINED3DERR_INVALIDCALL;
3162 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3163 /* Blit from render target to texture */
3164 WINED3DRECT srect;
3165 BOOL upsideDown, stretchx;
3166 BOOL paletteOverride = FALSE;
3168 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3169 TRACE("Color keying not supported by frame buffer to texture blit\n");
3170 return WINED3DERR_INVALIDCALL;
3171 /* Destination color key is checked above */
3174 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3175 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3177 if(SrcRect) {
3178 if(SrcRect->top < SrcRect->bottom) {
3179 srect.y1 = SrcRect->top;
3180 srect.y2 = SrcRect->bottom;
3181 upsideDown = FALSE;
3182 } else {
3183 srect.y1 = SrcRect->bottom;
3184 srect.y2 = SrcRect->top;
3185 upsideDown = TRUE;
3187 srect.x1 = SrcRect->left;
3188 srect.x2 = SrcRect->right;
3189 } else {
3190 srect.x1 = 0;
3191 srect.y1 = 0;
3192 srect.x2 = Src->currentDesc.Width;
3193 srect.y2 = Src->currentDesc.Height;
3194 upsideDown = FALSE;
3196 if(rect.x1 > rect.x2) {
3197 UINT tmp = rect.x2;
3198 rect.x2 = rect.x1;
3199 rect.x1 = tmp;
3200 upsideDown = !upsideDown;
3203 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3204 stretchx = TRUE;
3205 } else {
3206 stretchx = FALSE;
3209 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3210 * In this case grab the palette from the render target. */
3211 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3212 paletteOverride = TRUE;
3213 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3214 This->palette = Src->palette;
3217 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3218 * flip the image nor scale it.
3220 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3221 * -> If the app wants a image width an unscaled width, copy it line per line
3222 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3223 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3224 * back buffer. This is slower than reading line per line, thus not used for flipping
3225 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3226 * pixel by pixel
3228 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3229 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3230 * backends.
3232 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3233 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3234 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3235 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3236 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3237 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3238 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3239 } else {
3240 TRACE("Using hardware stretching to flip / stretch the texture\n");
3241 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3244 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3245 if(paletteOverride)
3246 This->palette = NULL;
3248 if(!(This->Flags & SFLAG_DONOTFREE)) {
3249 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3250 This->resource.allocatedMemory = NULL;
3251 This->resource.heapMemory = NULL;
3252 } else {
3253 This->Flags &= ~SFLAG_INSYSMEM;
3255 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3256 * path is never entered
3258 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3260 return WINED3D_OK;
3261 } else if(Src) {
3262 /* Blit from offscreen surface to render target */
3263 float glTexCoord[4];
3264 DWORD oldCKeyFlags = Src->CKeyFlags;
3265 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3266 RECT SourceRectangle;
3267 BOOL paletteOverride = FALSE;
3269 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3271 if(SrcRect) {
3272 SourceRectangle.left = SrcRect->left;
3273 SourceRectangle.right = SrcRect->right;
3274 SourceRectangle.top = SrcRect->top;
3275 SourceRectangle.bottom = SrcRect->bottom;
3276 } else {
3277 SourceRectangle.left = 0;
3278 SourceRectangle.right = Src->currentDesc.Width;
3279 SourceRectangle.top = 0;
3280 SourceRectangle.bottom = Src->currentDesc.Height;
3283 /* When blitting from an offscreen surface to a rendertarget, the source
3284 * surface is not required to have a palette. Our rendering / conversion
3285 * code further down the road retrieves the palette from the surface, so
3286 * it must have a palette set. */
3287 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3288 paletteOverride = TRUE;
3289 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3290 Src->palette = This->palette;
3293 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3294 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3295 TRACE("Using stretch_rect_fbo\n");
3296 /* The source is always a texture, but never the currently active render target, and the texture
3297 * contents are never upside down
3299 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3300 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3302 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3303 if(paletteOverride)
3304 Src->palette = NULL;
3305 return WINED3D_OK;
3308 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3309 /* Fall back to software */
3310 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3311 SourceRectangle.left, SourceRectangle.top,
3312 SourceRectangle.right, SourceRectangle.bottom);
3313 return WINED3DERR_INVALIDCALL;
3316 /* Color keying: Check if we have to do a color keyed blt,
3317 * and if not check if a color key is activated.
3319 * Just modify the color keying parameters in the surface and restore them afterwards
3320 * The surface keeps track of the color key last used to load the opengl surface.
3321 * PreLoad will catch the change to the flags and color key and reload if necessary.
3323 if(Flags & WINEDDBLT_KEYSRC) {
3324 /* Use color key from surface */
3325 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3326 /* Use color key from DDBltFx */
3327 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3328 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3329 } else {
3330 /* Do not use color key */
3331 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3334 /* Now load the surface */
3335 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3338 /* Activate the destination context, set it up for blitting */
3339 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3340 ENTER_GL();
3342 glEnable(Src->glDescription.target);
3343 checkGLcall("glEnable(Src->glDescription.target)");
3345 if(!dstSwapchain) {
3346 TRACE("Drawing to offscreen buffer\n");
3347 glDrawBuffer(myDevice->offscreenBuffer);
3348 checkGLcall("glDrawBuffer");
3349 } else {
3350 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3351 TRACE("Drawing to %#x buffer\n", buffer);
3352 glDrawBuffer(buffer);
3353 checkGLcall("glDrawBuffer");
3356 /* Bind the texture */
3357 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3358 checkGLcall("glBindTexture");
3360 /* Filtering for StretchRect */
3361 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3362 magLookup[Filter - WINED3DTEXF_NONE]);
3363 checkGLcall("glTexParameteri");
3364 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3365 minMipLookup[Filter][WINED3DTEXF_NONE]);
3366 checkGLcall("glTexParameteri");
3367 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3368 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3369 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3370 checkGLcall("glTexEnvi");
3372 /* This is for color keying */
3373 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3374 glEnable(GL_ALPHA_TEST);
3375 checkGLcall("glEnable GL_ALPHA_TEST");
3377 /* When the primary render target uses P8, the alpha component contains the palette index.
3378 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3379 * should be masked away have alpha set to 0. */
3380 if(primary_render_target_is_p8(myDevice))
3381 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3382 else
3383 glAlphaFunc(GL_NOTEQUAL, 0.0);
3384 checkGLcall("glAlphaFunc\n");
3385 } else {
3386 glDisable(GL_ALPHA_TEST);
3387 checkGLcall("glDisable GL_ALPHA_TEST");
3390 /* Draw a textured quad
3392 glBegin(GL_QUADS);
3394 glColor3d(1.0f, 1.0f, 1.0f);
3395 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3396 glVertex3f(rect.x1,
3397 rect.y1,
3398 0.0);
3400 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3401 glVertex3f(rect.x1, rect.y2, 0.0);
3403 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3404 glVertex3f(rect.x2,
3405 rect.y2,
3406 0.0);
3408 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3409 glVertex3f(rect.x2,
3410 rect.y1,
3411 0.0);
3412 glEnd();
3413 checkGLcall("glEnd");
3415 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3416 glDisable(GL_ALPHA_TEST);
3417 checkGLcall("glDisable(GL_ALPHA_TEST)");
3420 /* Flush in case the drawable is used by multiple GL contexts */
3421 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3422 glFlush();
3424 glBindTexture(Src->glDescription.target, 0);
3425 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3426 /* Leave the opengl state valid for blitting */
3427 glDisable(Src->glDescription.target);
3428 checkGLcall("glDisable(Src->glDescription.target)");
3430 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3431 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3433 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3434 glDrawBuffer(GL_BACK);
3435 checkGLcall("glDrawBuffer");
3437 /* Restore the color key parameters */
3438 Src->CKeyFlags = oldCKeyFlags;
3439 Src->SrcBltCKey = oldBltCKey;
3441 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3442 if(paletteOverride)
3443 Src->palette = NULL;
3445 LEAVE_GL();
3447 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3448 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3449 * is outdated now
3451 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3452 /* TODO: This should be moved to ModifyLocation() */
3453 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3454 This->Flags |= SFLAG_INTEXTURE;
3457 return WINED3D_OK;
3458 } else {
3459 /* Source-Less Blit to render target */
3460 if (Flags & WINEDDBLT_COLORFILL) {
3461 /* This is easy to handle for the D3D Device... */
3462 DWORD color;
3464 TRACE("Colorfill\n");
3466 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3467 must be true if we are here */
3468 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3469 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3470 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3471 TRACE("Surface is higher back buffer, falling back to software\n");
3472 return WINED3DERR_INVALIDCALL;
3475 /* The color as given in the Blt function is in the format of the frame-buffer...
3476 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3478 if (This->resource.format == WINED3DFMT_P8) {
3479 DWORD alpha;
3481 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3482 else alpha = 0xFF000000;
3484 if (This->palette) {
3485 color = (alpha |
3486 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3487 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3488 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3489 } else {
3490 color = alpha;
3493 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3494 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3495 color = 0xFFFFFFFF;
3496 } else {
3497 color = ((0xFF000000) |
3498 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3499 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3500 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3503 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3504 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3505 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3507 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3508 color = DDBltFx->u5.dwFillColor;
3510 else {
3511 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3512 return WINED3DERR_INVALIDCALL;
3515 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3516 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3517 1, /* Number of rectangles */
3518 &rect, WINED3DCLEAR_TARGET, color,
3519 0.0 /* Z */,
3520 0 /* Stencil */);
3521 return WINED3D_OK;
3525 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3526 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3527 return WINED3DERR_INVALIDCALL;
3530 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3532 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3533 float depth;
3535 if (Flags & WINEDDBLT_DEPTHFILL) {
3536 switch(This->resource.format) {
3537 case WINED3DFMT_D16:
3538 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3539 break;
3540 case WINED3DFMT_D15S1:
3541 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3542 break;
3543 case WINED3DFMT_D24S8:
3544 case WINED3DFMT_D24X8:
3545 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3546 break;
3547 case WINED3DFMT_D32:
3548 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3549 break;
3550 default:
3551 depth = 0.0;
3552 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3555 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3556 DestRect == NULL ? 0 : 1,
3557 (WINED3DRECT *) DestRect,
3558 WINED3DCLEAR_ZBUFFER,
3559 0x00000000,
3560 depth,
3561 0x00000000);
3564 FIXME("(%p): Unsupp depthstencil blit\n", This);
3565 return WINED3DERR_INVALIDCALL;
3568 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3569 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3570 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3571 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3572 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3573 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3575 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3577 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3578 return WINEDDERR_SURFACEBUSY;
3581 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3582 * except depth blits, which seem to work
3584 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3585 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3586 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3587 return WINED3DERR_INVALIDCALL;
3588 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3589 TRACE("Z Blit override handled the blit\n");
3590 return WINED3D_OK;
3594 /* Special cases for RenderTargets */
3595 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3596 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3597 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3600 /* For the rest call the X11 surface implementation.
3601 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3602 * other Blts are rather rare
3604 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3607 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3608 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3609 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3610 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3611 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3613 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3615 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3616 return WINEDDERR_SURFACEBUSY;
3619 if(myDevice->inScene &&
3620 (iface == myDevice->stencilBufferTarget ||
3621 (Source && Source == myDevice->stencilBufferTarget))) {
3622 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3623 return WINED3DERR_INVALIDCALL;
3626 /* Special cases for RenderTargets */
3627 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3628 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3630 RECT SrcRect, DstRect;
3631 DWORD Flags=0;
3633 if(rsrc) {
3634 SrcRect.left = rsrc->left;
3635 SrcRect.top= rsrc->top;
3636 SrcRect.bottom = rsrc->bottom;
3637 SrcRect.right = rsrc->right;
3638 } else {
3639 SrcRect.left = 0;
3640 SrcRect.top = 0;
3641 SrcRect.right = srcImpl->currentDesc.Width;
3642 SrcRect.bottom = srcImpl->currentDesc.Height;
3645 DstRect.left = dstx;
3646 DstRect.top=dsty;
3647 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3648 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3650 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3651 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3652 Flags |= WINEDDBLT_KEYSRC;
3653 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3654 Flags |= WINEDDBLT_KEYDEST;
3655 if(trans & WINEDDBLTFAST_WAIT)
3656 Flags |= WINEDDBLT_WAIT;
3657 if(trans & WINEDDBLTFAST_DONOTWAIT)
3658 Flags |= WINEDDBLT_DONOTWAIT;
3660 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3664 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3667 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3668 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3669 RGBQUAD col[256];
3670 IWineD3DPaletteImpl *pal = This->palette;
3671 unsigned int n;
3672 TRACE("(%p)\n", This);
3674 if (!pal) return WINED3D_OK;
3676 if(This->resource.format == WINED3DFMT_P8 ||
3677 This->resource.format == WINED3DFMT_A8P8)
3679 int bpp;
3680 GLenum format, internal, type;
3681 CONVERT_TYPES convert;
3683 /* Check if we are using a RTL mode which uses texturing for uploads */
3684 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3686 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3687 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3689 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3691 ENTER_GL();
3692 if (This->glDescription.textureName == 0) {
3693 glGenTextures(1, &This->glDescription.textureName);
3694 checkGLcall("glGenTextures");
3696 glBindTexture(This->glDescription.target, This->glDescription.textureName);
3697 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
3698 LEAVE_GL();
3700 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3701 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3703 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3704 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3706 /* Re-upload the palette */
3707 d3dfmt_p8_upload_palette(iface, convert);
3709 /* Without this some palette updates are missed. This at least happens on Nvidia drivers but
3710 * it works fine using Mesa. */
3711 ENTER_GL();
3712 glFlush();
3713 LEAVE_GL();
3714 } else {
3715 if(!(This->Flags & SFLAG_INSYSMEM)) {
3716 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3717 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3719 TRACE("Dirtifying surface\n");
3720 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3724 if(This->Flags & SFLAG_DIBSECTION) {
3725 TRACE("(%p): Updating the hdc's palette\n", This);
3726 for (n=0; n<256; n++) {
3727 col[n].rgbRed = pal->palents[n].peRed;
3728 col[n].rgbGreen = pal->palents[n].peGreen;
3729 col[n].rgbBlue = pal->palents[n].peBlue;
3730 col[n].rgbReserved = 0;
3732 SetDIBColorTable(This->hDC, 0, 256, col);
3735 /* Propagate the changes to the drawable when we have a palette. */
3736 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3737 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3739 return WINED3D_OK;
3742 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3743 /** Check against the maximum texture sizes supported by the video card **/
3744 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3745 unsigned int pow2Width, pow2Height;
3746 const GlPixelFormatDesc *glDesc;
3748 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3749 /* Setup some glformat defaults */
3750 This->glDescription.glFormat = glDesc->glFormat;
3751 This->glDescription.glFormatInternal = glDesc->glInternal;
3752 This->glDescription.glType = glDesc->glType;
3754 This->glDescription.textureName = 0;
3755 This->glDescription.target = GL_TEXTURE_2D;
3757 /* Non-power2 support */
3758 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3759 pow2Width = This->currentDesc.Width;
3760 pow2Height = This->currentDesc.Height;
3761 } else {
3762 /* Find the nearest pow2 match */
3763 pow2Width = pow2Height = 1;
3764 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3765 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3767 This->pow2Width = pow2Width;
3768 This->pow2Height = pow2Height;
3770 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3771 WINED3DFORMAT Format = This->resource.format;
3772 /** TODO: add support for non power two compressed textures **/
3773 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3774 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3775 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3776 This, This->currentDesc.Width, This->currentDesc.Height);
3777 return WINED3DERR_NOTAVAILABLE;
3781 if(pow2Width != This->currentDesc.Width ||
3782 pow2Height != This->currentDesc.Height) {
3783 This->Flags |= SFLAG_NONPOW2;
3786 TRACE("%p\n", This);
3787 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3788 /* one of three options
3789 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)
3790 2: Set the texture to the maximum size (bad idea)
3791 3: WARN and return WINED3DERR_NOTAVAILABLE;
3792 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.
3794 WARN("(%p) Creating an oversized surface\n", This);
3795 This->Flags |= SFLAG_OVERSIZE;
3797 /* This will be initialized on the first blt */
3798 This->glRect.left = 0;
3799 This->glRect.top = 0;
3800 This->glRect.right = 0;
3801 This->glRect.bottom = 0;
3802 } else {
3803 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3804 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3805 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3806 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3808 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3809 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3811 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3812 This->pow2Width = This->currentDesc.Width;
3813 This->pow2Height = This->currentDesc.Height;
3814 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3817 /* No oversize, gl rect is the full texture size */
3818 This->Flags &= ~SFLAG_OVERSIZE;
3819 This->glRect.left = 0;
3820 This->glRect.top = 0;
3821 This->glRect.right = This->pow2Width;
3822 This->glRect.bottom = This->pow2Height;
3825 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3826 switch(wined3d_settings.offscreen_rendering_mode) {
3827 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3828 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3829 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3833 This->Flags |= SFLAG_INSYSMEM;
3835 return WINED3D_OK;
3838 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
3839 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3841 TRACE("(%p) New location %#x\n", This, location);
3843 if (location & ~SFLAG_DS_LOCATIONS) {
3844 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
3847 This->Flags &= ~SFLAG_DS_LOCATIONS;
3848 This->Flags |= location;
3851 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
3852 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3853 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3855 TRACE("(%p) New location %#x\n", This, location);
3857 /* TODO: Make this work for modes other than FBO */
3858 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3860 if (This->Flags & location) {
3861 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
3862 return;
3865 if (This->current_renderbuffer) {
3866 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
3867 return;
3870 if (location == SFLAG_DS_OFFSCREEN) {
3871 if (This->Flags & SFLAG_DS_ONSCREEN) {
3872 GLint old_binding = 0;
3874 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
3876 ENTER_GL();
3878 if (!device->depth_blt_texture) {
3879 glGenTextures(1, &device->depth_blt_texture);
3882 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3883 * directly on the FBO texture. That's because we need to flip. */
3884 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
3885 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3886 glBindTexture(GL_TEXTURE_2D, device->depth_blt_texture);
3887 glCopyTexImage2D(This->glDescription.target,
3888 This->glDescription.level,
3889 This->glDescription.glFormatInternal,
3892 This->currentDesc.Width,
3893 This->currentDesc.Height,
3895 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3896 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3897 glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3898 glBindTexture(GL_TEXTURE_2D, old_binding);
3900 /* Setup the destination */
3901 if (!device->depth_blt_rb) {
3902 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
3903 checkGLcall("glGenRenderbuffersEXT");
3905 if (device->depth_blt_rb_w != This->currentDesc.Width
3906 || device->depth_blt_rb_h != This->currentDesc.Height) {
3907 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
3908 checkGLcall("glBindRenderbufferEXT");
3909 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
3910 checkGLcall("glRenderbufferStorageEXT");
3911 device->depth_blt_rb_w = This->currentDesc.Width;
3912 device->depth_blt_rb_h = This->currentDesc.Height;
3915 bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->dst_fbo);
3916 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
3917 checkGLcall("glFramebufferRenderbufferEXT");
3918 attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
3920 /* Do the actual blit */
3921 depth_blt((IWineD3DDevice *)device, device->depth_blt_texture);
3922 checkGLcall("depth_blt");
3924 if (device->render_offscreen) {
3925 bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->fbo);
3926 } else {
3927 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
3928 checkGLcall("glBindFramebuffer()");
3931 LEAVE_GL();
3932 } else {
3933 FIXME("No up to date depth stencil location\n");
3935 } else if (location == SFLAG_DS_ONSCREEN) {
3936 if (This->Flags & SFLAG_DS_OFFSCREEN) {
3937 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
3939 ENTER_GL();
3941 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
3942 checkGLcall("glBindFramebuffer()");
3943 depth_blt((IWineD3DDevice *)device, This->glDescription.textureName);
3944 checkGLcall("depth_blt");
3946 if (device->render_offscreen) {
3947 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->fbo));
3948 checkGLcall("glBindFramebuffer()");
3951 LEAVE_GL();
3952 } else {
3953 FIXME("No up to date depth stencil location\n");
3955 } else {
3956 ERR("(%p) Invalid location (%#x) specified\n", This, location);
3959 This->Flags |= location;
3962 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3963 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3964 IWineD3DBaseTexture *texture;
3966 TRACE("(%p)->(%s, %s)\n", iface,
3967 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3968 persistent ? "TRUE" : "FALSE");
3970 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3971 IWineD3DSwapChain *swapchain = NULL;
3973 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3974 TRACE("Surface %p is an onscreen surface\n", iface);
3976 IWineD3DSwapChain_Release(swapchain);
3977 } else {
3978 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3979 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3983 if(persistent) {
3984 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3985 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3986 TRACE("Passing to container\n");
3987 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3988 IWineD3DBaseTexture_Release(texture);
3991 This->Flags &= ~SFLAG_LOCATIONS;
3992 This->Flags |= flag;
3993 } else {
3994 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3995 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3996 TRACE("Passing to container\n");
3997 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3998 IWineD3DBaseTexture_Release(texture);
4001 This->Flags &= ~flag;
4005 struct coords {
4006 GLfloat x, y, z;
4009 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4010 struct coords coords[4];
4011 RECT rect;
4012 IWineD3DSwapChain *swapchain = NULL;
4013 IWineD3DBaseTexture *texture = NULL;
4014 HRESULT hr;
4015 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4017 if(rect_in) {
4018 rect = *rect_in;
4019 } else {
4020 rect.left = 0;
4021 rect.top = 0;
4022 rect.right = This->currentDesc.Width;
4023 rect.bottom = This->currentDesc.Height;
4026 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4027 ENTER_GL();
4029 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4030 glEnable(GL_TEXTURE_RECTANGLE_ARB);
4031 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
4032 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
4033 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
4034 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4035 checkGLcall("glTexParameteri");
4036 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4037 checkGLcall("glTexParameteri");
4039 coords[0].x = rect.left;
4040 coords[0].z = 0;
4042 coords[1].x = rect.left;
4043 coords[1].z = 0;
4045 coords[2].x = rect.right;
4046 coords[2].z = 0;
4048 coords[3].x = rect.right;
4049 coords[3].z = 0;
4051 coords[0].y = rect.top;
4052 coords[1].y = rect.bottom;
4053 coords[2].y = rect.bottom;
4054 coords[3].y = rect.top;
4055 } else if(This->glDescription.target == GL_TEXTURE_2D) {
4056 glEnable(GL_TEXTURE_2D);
4057 checkGLcall("glEnable(GL_TEXTURE_2D)");
4058 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
4059 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
4060 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4061 checkGLcall("glTexParameteri");
4062 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4063 checkGLcall("glTexParameteri");
4065 coords[0].x = (float)rect.left / This->pow2Width;
4066 coords[0].z = 0;
4068 coords[1].x = (float)rect.left / This->pow2Width;
4069 coords[1].z = 0;
4071 coords[2].x = (float)rect.right / This->pow2Width;
4072 coords[2].z = 0;
4074 coords[3].x = (float)rect.right / This->pow2Width;
4075 coords[3].z = 0;
4077 coords[0].y = (float)rect.top / This->pow2Height;
4078 coords[1].y = (float)rect.bottom / This->pow2Height;
4079 coords[2].y = (float)rect.bottom / This->pow2Height;
4080 coords[3].y = (float)rect.top / This->pow2Height;
4081 } else {
4082 /* Must be a cube map */
4083 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
4084 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
4085 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
4086 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
4087 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4088 checkGLcall("glTexParameteri");
4089 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4090 checkGLcall("glTexParameteri");
4092 switch(This->glDescription.target) {
4093 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4094 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
4095 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
4096 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
4097 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
4098 break;
4100 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4101 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4102 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
4103 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
4104 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4105 break;
4107 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4108 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
4109 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
4110 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
4111 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
4112 break;
4114 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4115 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4116 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
4117 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
4118 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4119 break;
4121 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4122 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4123 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
4124 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
4125 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
4126 break;
4128 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4129 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
4130 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
4131 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
4132 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4133 break;
4135 default:
4136 ERR("Unexpected texture target\n");
4137 LEAVE_GL();
4138 return;
4142 glBegin(GL_QUADS);
4143 glTexCoord3fv(&coords[0].x);
4144 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
4146 glTexCoord3fv(&coords[1].x);
4147 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
4149 glTexCoord3fv(&coords[2].x);
4150 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
4152 glTexCoord3fv(&coords[3].x);
4153 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
4154 glEnd();
4155 checkGLcall("glEnd");
4157 if(This->glDescription.target != GL_TEXTURE_2D) {
4158 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4159 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4160 } else {
4161 glDisable(GL_TEXTURE_2D);
4162 checkGLcall("glDisable(GL_TEXTURE_2D)");
4165 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
4166 if(hr == WINED3D_OK && swapchain) {
4167 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4168 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4169 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4170 glFlush();
4172 IWineD3DSwapChain_Release(swapchain);
4173 } else {
4174 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4175 * reset properly next draw
4177 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
4178 if(hr == WINED3D_OK && texture) {
4179 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4180 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4181 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4182 IWineD3DBaseTexture_Release(texture);
4185 LEAVE_GL();
4188 /*****************************************************************************
4189 * IWineD3DSurface::LoadLocation
4191 * Copies the current surface data from wherever it is to the requested
4192 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4193 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4194 * multiple locations, the gl texture is preferred over the drawable, which is
4195 * preferred over system memory. The PBO counts as system memory. If rect is
4196 * not NULL, only the specified rectangle is copied (only supported for
4197 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4198 * location is marked up to date after the copy.
4200 * Parameters:
4201 * flag: Surface location flag to be updated
4202 * rect: rectangle to be copied
4204 * Returns:
4205 * WINED3D_OK on success
4206 * WINED3DERR_DEVICELOST on an internal error
4208 *****************************************************************************/
4209 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4210 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4211 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4212 IWineD3DSwapChain *swapchain = NULL;
4213 GLenum format, internal, type;
4214 CONVERT_TYPES convert;
4215 int bpp;
4216 int width, pitch, outpitch;
4217 BYTE *mem;
4219 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4220 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4221 TRACE("Surface %p is an onscreen surface\n", iface);
4223 IWineD3DSwapChain_Release(swapchain);
4224 } else {
4225 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4226 * Prefer SFLAG_INTEXTURE. */
4227 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4231 TRACE("(%p)->(%s, %p)\n", iface,
4232 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4233 rect);
4234 if(rect) {
4235 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4238 if(This->Flags & flag) {
4239 TRACE("Location already up to date\n");
4240 return WINED3D_OK;
4243 if(!(This->Flags & SFLAG_LOCATIONS)) {
4244 ERR("Surface does not have any up to date location\n");
4245 This->Flags |= SFLAG_LOST;
4246 return WINED3DERR_DEVICELOST;
4249 if(flag == SFLAG_INSYSMEM) {
4250 surface_prepare_system_memory(This);
4252 /* Download the surface to system memory */
4253 if(This->Flags & SFLAG_INTEXTURE) {
4254 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4255 surface_bind_and_dirtify(This);
4257 surface_download_data(This);
4258 } else {
4259 read_from_framebuffer(This, rect,
4260 This->resource.allocatedMemory,
4261 IWineD3DSurface_GetPitch(iface));
4263 } else if(flag == SFLAG_INDRAWABLE) {
4264 if(This->Flags & SFLAG_INTEXTURE) {
4265 surface_blt_to_drawable(This, rect);
4266 } else {
4267 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4269 /* The width is in 'length' not in bytes */
4270 width = This->currentDesc.Width;
4271 pitch = IWineD3DSurface_GetPitch(iface);
4273 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4274 * but it isn't set (yet) in all cases it is getting called. */
4275 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4276 TRACE("Removing the pbo attached to surface %p\n", This);
4277 surface_remove_pbo(This);
4280 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4281 int height = This->currentDesc.Height;
4283 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4284 outpitch = width * bpp;
4285 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4287 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4288 if(!mem) {
4289 ERR("Out of memory %d, %d!\n", outpitch, height);
4290 return WINED3DERR_OUTOFVIDEOMEMORY;
4292 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4294 This->Flags |= SFLAG_CONVERTED;
4295 } else {
4296 This->Flags &= ~SFLAG_CONVERTED;
4297 mem = This->resource.allocatedMemory;
4300 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4302 /* Don't delete PBO memory */
4303 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4304 HeapFree(GetProcessHeap(), 0, mem);
4306 } else /* if(flag == SFLAG_INTEXTURE) */ {
4307 if (This->Flags & SFLAG_INDRAWABLE) {
4308 read_from_framebuffer_texture(This);
4309 } else { /* Upload from system memory */
4310 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4312 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4313 surface_bind_and_dirtify(This);
4314 ENTER_GL();
4316 /* The only place where LoadTexture() might get called when isInDraw=1
4317 * is ActivateContext where lastActiveRenderTarget is preloaded.
4319 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4320 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4322 /* Otherwise: System memory copy must be most up to date */
4324 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4325 This->Flags |= SFLAG_GLCKEY;
4326 This->glCKey = This->SrcBltCKey;
4328 else This->Flags &= ~SFLAG_GLCKEY;
4330 /* The width is in 'length' not in bytes */
4331 width = This->currentDesc.Width;
4332 pitch = IWineD3DSurface_GetPitch(iface);
4334 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4335 * but it isn't set (yet) in all cases it is getting called. */
4336 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4337 TRACE("Removing the pbo attached to surface %p\n", This);
4338 surface_remove_pbo(This);
4341 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4342 int height = This->currentDesc.Height;
4344 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4345 outpitch = width * bpp;
4346 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4348 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4349 if(!mem) {
4350 ERR("Out of memory %d, %d!\n", outpitch, height);
4351 return WINED3DERR_OUTOFVIDEOMEMORY;
4353 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4355 This->Flags |= SFLAG_CONVERTED;
4356 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4357 d3dfmt_p8_upload_palette(iface, convert);
4358 This->Flags &= ~SFLAG_CONVERTED;
4359 mem = This->resource.allocatedMemory;
4360 } else {
4361 This->Flags &= ~SFLAG_CONVERTED;
4362 mem = This->resource.allocatedMemory;
4365 /* Make sure the correct pitch is used */
4366 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4368 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4369 TRACE("non power of two support\n");
4370 if(!(This->Flags & SFLAG_ALLOCATED)) {
4371 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4373 if (mem || (This->Flags & SFLAG_PBO)) {
4374 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4376 } else {
4377 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4378 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4380 if(!(This->Flags & SFLAG_ALLOCATED)) {
4381 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4383 if (mem || (This->Flags & SFLAG_PBO)) {
4384 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4388 /* Restore the default pitch */
4389 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4390 LEAVE_GL();
4392 /* Don't delete PBO memory */
4393 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4394 HeapFree(GetProcessHeap(), 0, mem);
4398 if(rect == NULL) {
4399 This->Flags |= flag;
4402 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4403 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4404 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4405 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4408 return WINED3D_OK;
4411 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4412 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4413 IWineD3DSwapChain *swapchain = NULL;
4415 /* Update the drawable size method */
4416 if(container) {
4417 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4419 if(swapchain) {
4420 This->get_drawable_size = get_drawable_size_swapchain;
4421 IWineD3DSwapChain_Release(swapchain);
4422 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4423 switch(wined3d_settings.offscreen_rendering_mode) {
4424 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4425 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4426 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4430 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4433 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4434 return SURFACE_OPENGL;
4437 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4439 /* IUnknown */
4440 IWineD3DBaseSurfaceImpl_QueryInterface,
4441 IWineD3DBaseSurfaceImpl_AddRef,
4442 IWineD3DSurfaceImpl_Release,
4443 /* IWineD3DResource */
4444 IWineD3DBaseSurfaceImpl_GetParent,
4445 IWineD3DBaseSurfaceImpl_GetDevice,
4446 IWineD3DBaseSurfaceImpl_SetPrivateData,
4447 IWineD3DBaseSurfaceImpl_GetPrivateData,
4448 IWineD3DBaseSurfaceImpl_FreePrivateData,
4449 IWineD3DBaseSurfaceImpl_SetPriority,
4450 IWineD3DBaseSurfaceImpl_GetPriority,
4451 IWineD3DSurfaceImpl_PreLoad,
4452 IWineD3DSurfaceImpl_UnLoad,
4453 IWineD3DBaseSurfaceImpl_GetType,
4454 /* IWineD3DSurface */
4455 IWineD3DBaseSurfaceImpl_GetContainer,
4456 IWineD3DBaseSurfaceImpl_GetDesc,
4457 IWineD3DSurfaceImpl_LockRect,
4458 IWineD3DSurfaceImpl_UnlockRect,
4459 IWineD3DSurfaceImpl_GetDC,
4460 IWineD3DSurfaceImpl_ReleaseDC,
4461 IWineD3DSurfaceImpl_Flip,
4462 IWineD3DSurfaceImpl_Blt,
4463 IWineD3DBaseSurfaceImpl_GetBltStatus,
4464 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4465 IWineD3DBaseSurfaceImpl_IsLost,
4466 IWineD3DBaseSurfaceImpl_Restore,
4467 IWineD3DSurfaceImpl_BltFast,
4468 IWineD3DBaseSurfaceImpl_GetPalette,
4469 IWineD3DBaseSurfaceImpl_SetPalette,
4470 IWineD3DSurfaceImpl_RealizePalette,
4471 IWineD3DBaseSurfaceImpl_SetColorKey,
4472 IWineD3DBaseSurfaceImpl_GetPitch,
4473 IWineD3DSurfaceImpl_SetMem,
4474 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4475 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4476 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4477 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4478 IWineD3DBaseSurfaceImpl_SetClipper,
4479 IWineD3DBaseSurfaceImpl_GetClipper,
4480 /* Internal use: */
4481 IWineD3DSurfaceImpl_AddDirtyRect,
4482 IWineD3DSurfaceImpl_LoadTexture,
4483 IWineD3DSurfaceImpl_BindTexture,
4484 IWineD3DSurfaceImpl_SaveSnapshot,
4485 IWineD3DSurfaceImpl_SetContainer,
4486 IWineD3DSurfaceImpl_SetGlTextureDesc,
4487 IWineD3DSurfaceImpl_GetGlDesc,
4488 IWineD3DSurfaceImpl_GetData,
4489 IWineD3DSurfaceImpl_SetFormat,
4490 IWineD3DSurfaceImpl_PrivateSetup,
4491 IWineD3DSurfaceImpl_ModifyLocation,
4492 IWineD3DSurfaceImpl_LoadLocation,
4493 IWineD3DSurfaceImpl_GetImplType