- update wined3d/d3d8/d3d9 to Wine 1.1.15
[reactos.git] / reactos / dll / directx / wine / wined3d / surface.c
1 /*
2 * IWineD3DSurface Implementation
3 *
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 *
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.
18 *
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.
23 *
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
27 */
28
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
35 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
40
41 void surface_force_reload(IWineD3DSurface *iface)
42 {
43 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
44
45 This->Flags &= ~SFLAG_ALLOCATED;
46 }
47
48 void surface_set_texture_name(IWineD3DSurface *iface, GLuint name)
49 {
50 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
51
52 TRACE("(%p) : setting texture name %u\n", This, name);
53
54 if (!This->glDescription.textureName && name)
55 {
56 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
57 * surface has no texture name yet. See if we can get rid of this. */
58 if (This->Flags & SFLAG_INTEXTURE)
59 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
60 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
61 }
62
63 This->glDescription.textureName = name;
64 surface_force_reload(iface);
65 }
66
67 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
68 {
69 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
70
71 TRACE("(%p) : setting target %#x\n", This, target);
72
73 if (This->glDescription.target != target)
74 {
75 if (target == GL_TEXTURE_RECTANGLE_ARB)
76 {
77 This->Flags &= ~SFLAG_NORMCOORD;
78 }
79 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
80 {
81 This->Flags |= SFLAG_NORMCOORD;
82 }
83 }
84 This->glDescription.target = target;
85 surface_force_reload(iface);
86 }
87
88 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
89 int active_sampler;
90
91 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
92 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
93 * gl states. The current texture unit should always be a valid one.
94 *
95 * To be more specific, this is tricky because we can implicitly be called
96 * from sampler() in state.c. This means we can't touch anything other than
97 * whatever happens to be the currently active texture, or we would risk
98 * marking already applied sampler states dirty again.
99 *
100 * TODO: Track the current active texture per GL context instead of using glGet
101 */
102 GLint active_texture;
103 ENTER_GL();
104 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
105 LEAVE_GL();
106 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
107
108 if (active_sampler != -1) {
109 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
110 }
111 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
112 }
113
114 /* This function checks if the primary render target uses the 8bit paletted format. */
115 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
116 {
117 if (device->render_targets && device->render_targets[0]) {
118 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
119 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
120 return TRUE;
121 }
122 return FALSE;
123 }
124
125 /* This call just downloads data, the caller is responsible for activating the
126 * right context and binding the correct texture. */
127 static void surface_download_data(IWineD3DSurfaceImpl *This) {
128 if (0 == This->glDescription.textureName) {
129 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
130 return;
131 }
132
133 /* Only support read back of converted P8 surfaces */
134 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
135 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
136 return;
137 }
138
139 ENTER_GL();
140
141 if (This->resource.format == WINED3DFMT_DXT1 ||
142 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
143 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
144 This->resource.format == WINED3DFMT_ATI2N) {
145 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
146 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
147 } else {
148 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
149 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
150
151 if(This->Flags & SFLAG_PBO) {
152 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
153 checkGLcall("glBindBufferARB");
154 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
155 checkGLcall("glGetCompressedTexImageARB()");
156 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
157 checkGLcall("glBindBufferARB");
158 } else {
159 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
160 checkGLcall("glGetCompressedTexImageARB()");
161 }
162 }
163 LEAVE_GL();
164 } else {
165 void *mem;
166 GLenum format = This->glDescription.glFormat;
167 GLenum type = This->glDescription.glType;
168 int src_pitch = 0;
169 int dst_pitch = 0;
170
171 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
172 if(This->resource.format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice)) {
173 format = GL_ALPHA;
174 type = GL_UNSIGNED_BYTE;
175 }
176
177 if (This->Flags & SFLAG_NONPOW2) {
178 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
179 src_pitch = This->bytesPerPixel * This->pow2Width;
180 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
181 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
182 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
183 } else {
184 mem = This->resource.allocatedMemory;
185 }
186
187 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
188 format, type, mem);
189
190 if(This->Flags & SFLAG_PBO) {
191 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
192 checkGLcall("glBindBufferARB");
193
194 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
195 type, NULL);
196 checkGLcall("glGetTexImage()");
197
198 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
199 checkGLcall("glBindBufferARB");
200 } else {
201 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
202 type, mem);
203 checkGLcall("glGetTexImage()");
204 }
205 LEAVE_GL();
206
207 if (This->Flags & SFLAG_NONPOW2) {
208 const BYTE *src_data;
209 BYTE *dst_data;
210 UINT y;
211 /*
212 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
213 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
214 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
215 *
216 * We're doing this...
217 *
218 * instead of boxing the texture :
219 * |<-texture width ->| -->pow2width| /\
220 * |111111111111111111| | |
221 * |222 Texture 222222| boxed empty | texture height
222 * |3333 Data 33333333| | |
223 * |444444444444444444| | \/
224 * ----------------------------------- |
225 * | boxed empty | boxed empty | pow2height
226 * | | | \/
227 * -----------------------------------
228 *
229 *
230 * we're repacking the data to the expected texture width
231 *
232 * |<-texture width ->| -->pow2width| /\
233 * |111111111111111111222222222222222| |
234 * |222333333333333333333444444444444| texture height
235 * |444444 | |
236 * | | \/
237 * | | |
238 * | empty | pow2height
239 * | | \/
240 * -----------------------------------
241 *
242 * == is the same as
243 *
244 * |<-texture width ->| /\
245 * |111111111111111111|
246 * |222222222222222222|texture height
247 * |333333333333333333|
248 * |444444444444444444| \/
249 * --------------------
250 *
251 * this also means that any references to allocatedMemory should work with the data as if were a
252 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
253 *
254 * internally the texture is still stored in a boxed format so any references to textureName will
255 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
256 *
257 * Performance should not be an issue, because applications normally do not lock the surfaces when
258 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
259 * and doesn't have to be re-read.
260 */
261 src_data = mem;
262 dst_data = This->resource.allocatedMemory;
263 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
264 for (y = 1 ; y < This->currentDesc.Height; y++) {
265 /* skip the first row */
266 src_data += src_pitch;
267 dst_data += dst_pitch;
268 memcpy(dst_data, src_data, dst_pitch);
269 }
270
271 HeapFree(GetProcessHeap(), 0, mem);
272 }
273 }
274
275 /* Surface has now been downloaded */
276 This->Flags |= SFLAG_INSYSMEM;
277 }
278
279 /* This call just uploads data, the caller is responsible for activating the
280 * right context and binding the correct texture. */
281 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
282
283 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
284
285 if (This->resource.format == WINED3DFMT_DXT1 ||
286 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
287 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
288 This->resource.format == WINED3DFMT_ATI2N) {
289 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
290 FIXME("Using DXT1/3/5 without advertized support\n");
291 } else {
292 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
293 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
294 * function uses glCompressedTexImage2D instead of the SubImage call
295 */
296 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
297 ENTER_GL();
298
299 if(This->Flags & SFLAG_PBO) {
300 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
301 checkGLcall("glBindBufferARB");
302 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
303
304 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
305 width, height, 0 /* border */, This->resource.size, NULL));
306 checkGLcall("glCompressedTexSubImage2D");
307
308 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
309 checkGLcall("glBindBufferARB");
310 } else {
311 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
312 width, height, 0 /* border */, This->resource.size, data));
313 checkGLcall("glCompressedTexSubImage2D");
314 }
315 LEAVE_GL();
316 }
317 } else {
318 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
319 ENTER_GL();
320
321 if(This->Flags & SFLAG_PBO) {
322 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
323 checkGLcall("glBindBufferARB");
324 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
325
326 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
327 checkGLcall("glTexSubImage2D");
328
329 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
330 checkGLcall("glBindBufferARB");
331 }
332 else {
333 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
334 checkGLcall("glTexSubImage2D");
335 }
336
337 LEAVE_GL();
338 }
339 }
340
341 /* This call just allocates the texture, the caller is responsible for
342 * activating the right context and binding the correct texture. */
343 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
344 BOOL enable_client_storage = FALSE;
345 const BYTE *mem = NULL;
346
347 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
348
349 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,
350 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
351
352 if (This->resource.format == WINED3DFMT_DXT1 ||
353 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
354 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
355 This->resource.format == WINED3DFMT_ATI2N) {
356 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
357 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
358
359 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
360 * once, unfortunately
361 */
362 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
363 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
364 This->Flags |= SFLAG_CLIENT;
365 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
366 ENTER_GL();
367 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
368 width, height, 0 /* border */, This->resource.size, mem));
369 LEAVE_GL();
370 }
371
372 return;
373 }
374
375 ENTER_GL();
376
377 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
378 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
379 /* In some cases we want to disable client storage.
380 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
381 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
382 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
383 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
384 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
385 */
386 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
387 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
388 This->Flags &= ~SFLAG_CLIENT;
389 enable_client_storage = TRUE;
390 } else {
391 This->Flags |= SFLAG_CLIENT;
392
393 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
394 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
395 */
396 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
397 }
398 }
399 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
400 checkGLcall("glTexImage2D");
401
402 if(enable_client_storage) {
403 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
404 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
405 }
406 LEAVE_GL();
407
408 This->Flags |= SFLAG_ALLOCATED;
409 }
410
411 /* In D3D the depth stencil dimensions have to be greater than or equal to the
412 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
413 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
414 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
415 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
416 renderbuffer_entry_t *entry;
417 GLuint renderbuffer = 0;
418 unsigned int src_width, src_height;
419
420 src_width = This->pow2Width;
421 src_height = This->pow2Height;
422
423 /* A depth stencil smaller than the render target is not valid */
424 if (width > src_width || height > src_height) return;
425
426 /* Remove any renderbuffer set if the sizes match */
427 if (width == src_width && height == src_height) {
428 This->current_renderbuffer = NULL;
429 return;
430 }
431
432 /* Look if we've already got a renderbuffer of the correct dimensions */
433 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
434 if (entry->width == width && entry->height == height) {
435 renderbuffer = entry->id;
436 This->current_renderbuffer = entry;
437 break;
438 }
439 }
440
441 if (!renderbuffer) {
442 const struct GlPixelFormatDesc *glDesc;
443 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
444
445 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
446 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
447 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glInternal, width, height));
448
449 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
450 entry->width = width;
451 entry->height = height;
452 entry->id = renderbuffer;
453 list_add_head(&This->renderbuffers, &entry->entry);
454
455 This->current_renderbuffer = entry;
456 }
457
458 checkGLcall("set_compatible_renderbuffer");
459 }
460
461 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
462 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
463 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
464
465 TRACE("(%p) : swapchain %p\n", This, swapchain);
466
467 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
468 TRACE("Returning GL_BACK\n");
469 return GL_BACK;
470 } else if (swapchain_impl->frontBuffer == iface) {
471 TRACE("Returning GL_FRONT\n");
472 return GL_FRONT;
473 }
474
475 FIXME("Higher back buffer, returning GL_BACK\n");
476 return GL_BACK;
477 }
478
479 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
480 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
481 {
482 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
483 IWineD3DBaseTexture *baseTexture = NULL;
484
485 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
486 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
487
488 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
489 if (dirty_rect)
490 {
491 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
492 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
493 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
494 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
495 }
496 else
497 {
498 This->dirtyRect.left = 0;
499 This->dirtyRect.top = 0;
500 This->dirtyRect.right = This->currentDesc.Width;
501 This->dirtyRect.bottom = This->currentDesc.Height;
502 }
503
504 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
505 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
506
507 /* if the container is a basetexture then mark it dirty. */
508 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
509 {
510 TRACE("Passing to container\n");
511 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
512 IWineD3DBaseTexture_Release(baseTexture);
513 }
514 }
515
516 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
517 {
518 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
519 ULONG ref = InterlockedDecrement(&This->resource.ref);
520 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
521 if (ref == 0) {
522 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
523 renderbuffer_entry_t *entry, *entry2;
524 TRACE("(%p) : cleaning up\n", This);
525
526 /* Need a context to destroy the texture. Use the currently active render target, but only if
527 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
528 * When destroying the primary rt, Uninit3D will activate a context before doing anything
529 */
530 if(device->render_targets && device->render_targets[0]) {
531 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
532 }
533
534 ENTER_GL();
535 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
536 TRACE("Deleting texture %d\n", This->glDescription.textureName);
537 glDeleteTextures(1, &This->glDescription.textureName);
538 }
539
540 if(This->Flags & SFLAG_PBO) {
541 /* Delete the PBO */
542 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
543 }
544
545 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
546 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
547 HeapFree(GetProcessHeap(), 0, entry);
548 }
549 LEAVE_GL();
550
551 if(This->Flags & SFLAG_DIBSECTION) {
552 /* Release the DC */
553 SelectObject(This->hDC, This->dib.holdbitmap);
554 DeleteDC(This->hDC);
555 /* Release the DIB section */
556 DeleteObject(This->dib.DIBsection);
557 This->dib.bitmap_data = NULL;
558 This->resource.allocatedMemory = NULL;
559 }
560 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
561
562 HeapFree(GetProcessHeap(), 0, This->palette9);
563
564 resource_cleanup((IWineD3DResource *)iface);
565
566 if(This->overlay_dest) {
567 list_remove(&This->overlay_entry);
568 }
569
570 TRACE("(%p) Released\n", This);
571 HeapFree(GetProcessHeap(), 0, This);
572
573 }
574 return ref;
575 }
576
577 /* ****************************************************
578 IWineD3DSurface IWineD3DResource parts follow
579 **************************************************** */
580
581 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface)
582 {
583 /* TODO: check for locks */
584 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
585 IWineD3DBaseTexture *baseTexture = NULL;
586 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
587
588 TRACE("(%p)Checking to see if the container is a base texture\n", This);
589 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
590 TRACE("Passing to container\n");
591 IWineD3DBaseTexture_PreLoad(baseTexture);
592 IWineD3DBaseTexture_Release(baseTexture);
593 } else {
594 TRACE("(%p) : About to load surface\n", This);
595
596 if(!device->isInDraw) {
597 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
598 }
599
600 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
601 if(palette9_changed(This)) {
602 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
603 /* TODO: This is not necessarily needed with hw palettized texture support */
604 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
605 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
606 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
607 }
608 }
609
610 IWineD3DSurface_LoadTexture(iface, FALSE);
611
612 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
613 /* Tell opengl to try and keep this texture in video ram (well mostly) */
614 GLclampf tmp;
615 tmp = 0.9f;
616 ENTER_GL();
617 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
618 LEAVE_GL();
619 }
620 }
621 return;
622 }
623
624 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
625 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
626 This->resource.allocatedMemory =
627 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
628
629 ENTER_GL();
630 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
631 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
632 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
633 checkGLcall("glGetBufferSubData");
634 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
635 checkGLcall("glDeleteBuffers");
636 LEAVE_GL();
637
638 This->pbo = 0;
639 This->Flags &= ~SFLAG_PBO;
640 }
641
642 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
643 IWineD3DBaseTexture *texture = NULL;
644 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
645 renderbuffer_entry_t *entry, *entry2;
646 TRACE("(%p)\n", iface);
647
648 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
649 /* Default pool resources are supposed to be destroyed before Reset is called.
650 * Implicit resources stay however. So this means we have an implicit render target
651 * or depth stencil. The content may be destroyed, but we still have to tear down
652 * opengl resources, so we cannot leave early.
653 *
654 * Put the most up to date surface location into the drawable. D3D-wise this content
655 * is undefined, so it would be nowhere, but that would make the location management
656 * more complicated. The drawable is a sane location, because if we mark sysmem or
657 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
658 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
659 * sysmem copy here.
660 */
661 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
662 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
663 } else {
664 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
665 }
666 } else {
667 /* Load the surface into system memory */
668 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
669 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
670 }
671 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
672 This->Flags &= ~SFLAG_ALLOCATED;
673
674 /* Destroy PBOs, but load them into real sysmem before */
675 if(This->Flags & SFLAG_PBO) {
676 surface_remove_pbo(This);
677 }
678
679 /* Destroy fbo render buffers. This is needed for implicit render targets, for
680 * all application-created targets the application has to release the surface
681 * before calling _Reset
682 */
683 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
684 ENTER_GL();
685 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
686 LEAVE_GL();
687 list_remove(&entry->entry);
688 HeapFree(GetProcessHeap(), 0, entry);
689 }
690 list_init(&This->renderbuffers);
691 This->current_renderbuffer = NULL;
692
693 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
694 * destroy it
695 */
696 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
697 if(!texture) {
698 ENTER_GL();
699 glDeleteTextures(1, &This->glDescription.textureName);
700 This->glDescription.textureName = 0;
701 LEAVE_GL();
702 } else {
703 IWineD3DBaseTexture_Release(texture);
704 }
705 return;
706 }
707
708 /* ******************************************************
709 IWineD3DSurface IWineD3DSurface parts follow
710 ****************************************************** */
711
712 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
713 {
714 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
715 TRACE("(%p) : returning %p\n", This, &This->glDescription);
716 *glDescription = &This->glDescription;
717 }
718
719 /* Read the framebuffer back into the surface */
720 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
721 IWineD3DSwapChainImpl *swapchain;
722 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
723 BYTE *mem;
724 GLint fmt;
725 GLint type;
726 BYTE *row, *top, *bottom;
727 int i;
728 BOOL bpp;
729 RECT local_rect;
730 BOOL srcIsUpsideDown;
731 GLint rowLen = 0;
732 GLint skipPix = 0;
733 GLint skipRow = 0;
734
735 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
736 static BOOL warned = FALSE;
737 if(!warned) {
738 ERR("The application tries to lock the render target, but render target locking is disabled\n");
739 warned = TRUE;
740 }
741 return;
742 }
743
744 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
745 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
746 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
747 * context->last_was_blit set on the unlock.
748 */
749 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
750 ENTER_GL();
751
752 /* Select the correct read buffer, and give some debug output.
753 * There is no need to keep track of the current read buffer or reset it, every part of the code
754 * that reads sets the read buffer as desired.
755 */
756 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
757 {
758 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
759 TRACE("Locking %#x buffer\n", buffer);
760 glReadBuffer(buffer);
761 checkGLcall("glReadBuffer");
762
763 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
764 srcIsUpsideDown = FALSE;
765 } else {
766 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
767 * Read from the back buffer
768 */
769 TRACE("Locking offscreen render target\n");
770 glReadBuffer(myDevice->offscreenBuffer);
771 srcIsUpsideDown = TRUE;
772 }
773
774 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
775 if(!rect) {
776 local_rect.left = 0;
777 local_rect.top = 0;
778 local_rect.right = This->currentDesc.Width;
779 local_rect.bottom = This->currentDesc.Height;
780 } else {
781 local_rect = *rect;
782 }
783 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
784
785 switch(This->resource.format)
786 {
787 case WINED3DFMT_P8:
788 {
789 if(primary_render_target_is_p8(myDevice)) {
790 /* In case of P8 render targets the index is stored in the alpha component */
791 fmt = GL_ALPHA;
792 type = GL_UNSIGNED_BYTE;
793 mem = dest;
794 bpp = This->bytesPerPixel;
795 } else {
796 /* GL can't return palettized data, so read ARGB pixels into a
797 * separate block of memory and convert them into palettized format
798 * in software. Slow, but if the app means to use palettized render
799 * targets and locks it...
800 *
801 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
802 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
803 * for the color channels when palettizing the colors.
804 */
805 fmt = GL_RGB;
806 type = GL_UNSIGNED_BYTE;
807 pitch *= 3;
808 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
809 if(!mem) {
810 ERR("Out of memory\n");
811 LEAVE_GL();
812 return;
813 }
814 bpp = This->bytesPerPixel * 3;
815 }
816 }
817 break;
818
819 default:
820 mem = dest;
821 fmt = This->glDescription.glFormat;
822 type = This->glDescription.glType;
823 bpp = This->bytesPerPixel;
824 }
825
826 if(This->Flags & SFLAG_PBO) {
827 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
828 checkGLcall("glBindBufferARB");
829 if(mem != NULL) {
830 ERR("mem not null for pbo -- unexpected\n");
831 mem = NULL;
832 }
833 }
834
835 /* Save old pixel store pack state */
836 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
837 checkGLcall("glIntegerv");
838 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
839 checkGLcall("glIntegerv");
840 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
841 checkGLcall("glIntegerv");
842
843 /* Setup pixel store pack state -- to glReadPixels into the correct place */
844 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
845 checkGLcall("glPixelStorei");
846 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
847 checkGLcall("glPixelStorei");
848 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
849 checkGLcall("glPixelStorei");
850
851 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
852 local_rect.right - local_rect.left,
853 local_rect.bottom - local_rect.top,
854 fmt, type, mem);
855 checkGLcall("glReadPixels");
856
857 /* Reset previous pixel store pack state */
858 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
859 checkGLcall("glPixelStorei");
860 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
861 checkGLcall("glPixelStorei");
862 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
863 checkGLcall("glPixelStorei");
864
865 if(This->Flags & SFLAG_PBO) {
866 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
867 checkGLcall("glBindBufferARB");
868
869 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
870 * to get a pointer to it and perform the flipping in software. This is a lot
871 * faster than calling glReadPixels for each line. In case we want more speed
872 * we should rerender it flipped in a FBO and read the data back from the FBO. */
873 if(!srcIsUpsideDown) {
874 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
875 checkGLcall("glBindBufferARB");
876
877 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
878 checkGLcall("glMapBufferARB");
879 }
880 }
881
882 /* TODO: Merge this with the palettization loop below for P8 targets */
883 if(!srcIsUpsideDown) {
884 UINT len, off;
885 /* glReadPixels returns the image upside down, and there is no way to prevent this.
886 Flip the lines in software */
887 len = (local_rect.right - local_rect.left) * bpp;
888 off = local_rect.left * bpp;
889
890 row = HeapAlloc(GetProcessHeap(), 0, len);
891 if(!row) {
892 ERR("Out of memory\n");
893 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
894 LEAVE_GL();
895 return;
896 }
897
898 top = mem + pitch * local_rect.top;
899 bottom = mem + pitch * (local_rect.bottom - 1);
900 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
901 memcpy(row, top + off, len);
902 memcpy(top + off, bottom + off, len);
903 memcpy(bottom + off, row, len);
904 top += pitch;
905 bottom -= pitch;
906 }
907 HeapFree(GetProcessHeap(), 0, row);
908
909 /* Unmap the temp PBO buffer */
910 if(This->Flags & SFLAG_PBO) {
911 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
912 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
913 }
914 }
915
916 LEAVE_GL();
917
918 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
919 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
920 * the same color but we have no choice.
921 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
922 */
923 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
924 const PALETTEENTRY *pal = NULL;
925 DWORD width = pitch / 3;
926 int x, y, c;
927
928 if(This->palette) {
929 pal = This->palette->palents;
930 } else {
931 ERR("Palette is missing, cannot perform inverse palette lookup\n");
932 HeapFree(GetProcessHeap(), 0, mem);
933 return ;
934 }
935
936 for(y = local_rect.top; y < local_rect.bottom; y++) {
937 for(x = local_rect.left; x < local_rect.right; x++) {
938 /* start lines pixels */
939 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
940 const BYTE *green = blue + 1;
941 const BYTE *red = green + 1;
942
943 for(c = 0; c < 256; c++) {
944 if(*red == pal[c].peRed &&
945 *green == pal[c].peGreen &&
946 *blue == pal[c].peBlue)
947 {
948 *((BYTE *) dest + y * width + x) = c;
949 break;
950 }
951 }
952 }
953 }
954 HeapFree(GetProcessHeap(), 0, mem);
955 }
956 }
957
958 /* Read the framebuffer contents into a texture */
959 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
960 {
961 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
962 IWineD3DSwapChainImpl *swapchain;
963 int bpp;
964 GLenum format, internal, type;
965 CONVERT_TYPES convert;
966 GLint prevRead;
967
968 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
969
970 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
971 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
972 * states in the stateblock, and no driver was found yet that had bugs in that regard.
973 */
974 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
975 surface_bind_and_dirtify(This);
976
977 ENTER_GL();
978 glGetIntegerv(GL_READ_BUFFER, &prevRead);
979 LEAVE_GL();
980
981 /* Select the correct read buffer, and give some debug output.
982 * There is no need to keep track of the current read buffer or reset it, every part of the code
983 * that reads sets the read buffer as desired.
984 */
985 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
986 {
987 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
988 TRACE("Locking %#x buffer\n", buffer);
989
990 ENTER_GL();
991 glReadBuffer(buffer);
992 checkGLcall("glReadBuffer");
993 LEAVE_GL();
994
995 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
996 } else {
997 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
998 * Read from the back buffer
999 */
1000 TRACE("Locking offscreen render target\n");
1001 ENTER_GL();
1002 glReadBuffer(device->offscreenBuffer);
1003 checkGLcall("glReadBuffer");
1004 LEAVE_GL();
1005 }
1006
1007 if(!(This->Flags & SFLAG_ALLOCATED)) {
1008 surface_allocate_surface(This, internal, This->pow2Width,
1009 This->pow2Height, format, type);
1010 }
1011
1012 ENTER_GL();
1013 /* If !SrcIsUpsideDown we should flip the surface.
1014 * This can be done using glCopyTexSubImage2D but this
1015 * is VERY slow, so don't do that. We should prevent
1016 * this code from getting called in such cases or perhaps
1017 * we can use FBOs */
1018
1019 glCopyTexSubImage2D(This->glDescription.target,
1020 This->glDescription.level,
1021 0, 0, 0, 0,
1022 This->currentDesc.Width,
1023 This->currentDesc.Height);
1024 checkGLcall("glCopyTexSubImage2D");
1025
1026 glReadBuffer(prevRead);
1027 checkGLcall("glReadBuffer");
1028
1029 LEAVE_GL();
1030 TRACE("Updated target %d\n", This->glDescription.target);
1031 }
1032
1033 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1034 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1035 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1036 * changed
1037 */
1038 if(!(This->Flags & SFLAG_DYNLOCK)) {
1039 This->lockCount++;
1040 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1041 if(This->lockCount > MAXLOCKCOUNT) {
1042 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1043 This->Flags |= SFLAG_DYNLOCK;
1044 }
1045 }
1046
1047 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1048 * Also don't create a PBO for systemmem surfaces.
1049 */
1050 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1051 GLenum error;
1052 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1053
1054 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1055 ENTER_GL();
1056
1057 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1058 error = glGetError();
1059 if(This->pbo == 0 || error != GL_NO_ERROR) {
1060 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1061 }
1062
1063 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1064
1065 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1066 checkGLcall("glBindBufferARB");
1067
1068 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1069 checkGLcall("glBufferDataARB");
1070
1071 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1072 checkGLcall("glBindBufferARB");
1073
1074 /* We don't need the system memory anymore and we can't even use it for PBOs */
1075 if(!(This->Flags & SFLAG_CLIENT)) {
1076 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1077 This->resource.heapMemory = NULL;
1078 }
1079 This->resource.allocatedMemory = NULL;
1080 This->Flags |= SFLAG_PBO;
1081 LEAVE_GL();
1082 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1083 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1084 * or a pbo to map
1085 */
1086 if(!This->resource.heapMemory) {
1087 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1088 }
1089 This->resource.allocatedMemory =
1090 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1091 if(This->Flags & SFLAG_INSYSMEM) {
1092 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1093 }
1094 }
1095 }
1096
1097 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1098 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1099 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1100 IWineD3DSwapChain *swapchain = NULL;
1101
1102 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1103
1104 /* This is also done in the base class, but we have to verify this before loading any data from
1105 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1106 * may interfere, and all other bad things may happen
1107 */
1108 if (This->Flags & SFLAG_LOCKED) {
1109 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1110 return WINED3DERR_INVALIDCALL;
1111 }
1112 This->Flags |= SFLAG_LOCKED;
1113
1114 if (!(This->Flags & SFLAG_LOCKABLE))
1115 {
1116 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1117 }
1118
1119 if (Flags & WINED3DLOCK_DISCARD) {
1120 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1121 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1122 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1123 This->Flags |= SFLAG_INSYSMEM;
1124 goto lock_end;
1125 }
1126
1127 if (This->Flags & SFLAG_INSYSMEM) {
1128 TRACE("Local copy is up to date, not downloading data\n");
1129 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1130 goto lock_end;
1131 }
1132
1133 /* Now download the surface content from opengl
1134 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1135 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1136 */
1137 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1138 if(swapchain || iface == myDevice->render_targets[0]) {
1139 const RECT *pass_rect = pRect;
1140
1141 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1142 * because most caller functions do not need that. So do that here
1143 */
1144 if(pRect &&
1145 pRect->top == 0 &&
1146 pRect->left == 0 &&
1147 pRect->right == This->currentDesc.Width &&
1148 pRect->bottom == This->currentDesc.Height) {
1149 pass_rect = NULL;
1150 }
1151
1152 switch(wined3d_settings.rendertargetlock_mode) {
1153 case RTL_TEXDRAW:
1154 case RTL_TEXTEX:
1155 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1156 #if 0
1157 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1158 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1159 * This may be faster on some cards
1160 */
1161 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1162 #endif
1163 /* drop through */
1164
1165 case RTL_AUTO:
1166 case RTL_READDRAW:
1167 case RTL_READTEX:
1168 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1169 break;
1170
1171 case RTL_DISABLE:
1172 break;
1173 }
1174 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1175
1176 } else if(iface == myDevice->stencilBufferTarget) {
1177 /** the depth stencil in openGL has a format of GL_FLOAT
1178 * which should be good for WINED3DFMT_D16_LOCKABLE
1179 * and WINED3DFMT_D16
1180 * it is unclear what format the stencil buffer is in except.
1181 * 'Each index is converted to fixed point...
1182 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1183 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1184 * glReadPixels(This->lockedRect.left,
1185 * This->lockedRect.bottom - j - 1,
1186 * This->lockedRect.right - This->lockedRect.left,
1187 * 1,
1188 * GL_DEPTH_COMPONENT,
1189 * type,
1190 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1191 *
1192 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1193 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1194 * none of that is the case the problem is not in this function :-)
1195 ********************************************/
1196 FIXME("Depth stencil locking not supported yet\n");
1197 } else {
1198 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1199 TRACE("locking an ordinary surface\n");
1200 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1201 }
1202
1203 lock_end:
1204 if(This->Flags & SFLAG_PBO) {
1205 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1206 ENTER_GL();
1207 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1208 checkGLcall("glBindBufferARB");
1209
1210 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1211 if(This->resource.allocatedMemory) {
1212 ERR("The surface already has PBO memory allocated!\n");
1213 }
1214
1215 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1216 checkGLcall("glMapBufferARB");
1217
1218 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1219 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1220 checkGLcall("glBindBufferARB");
1221
1222 LEAVE_GL();
1223 }
1224
1225 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1226 /* Don't dirtify */
1227 } else {
1228 IWineD3DBaseTexture *pBaseTexture;
1229 /**
1230 * Dirtify on lock
1231 * as seen in msdn docs
1232 */
1233 surface_add_dirty_rect(iface, pRect);
1234
1235 /** Dirtify Container if needed */
1236 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1237 TRACE("Making container dirty\n");
1238 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1239 IWineD3DBaseTexture_Release(pBaseTexture);
1240 } else {
1241 TRACE("Surface is standalone, no need to dirty the container\n");
1242 }
1243 }
1244
1245 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1246 }
1247
1248 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1249 GLint prev_store;
1250 GLint prev_rasterpos[4];
1251 GLint skipBytes = 0;
1252 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1253 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1254 IWineD3DSwapChainImpl *swapchain;
1255
1256 /* Activate the correct context for the render target */
1257 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1258 ENTER_GL();
1259
1260 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1261 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1262 TRACE("Unlocking %#x buffer\n", buffer);
1263 glDrawBuffer(buffer);
1264 checkGLcall("glDrawBuffer");
1265
1266 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1267 } else {
1268 /* Primary offscreen render target */
1269 TRACE("Offscreen render target\n");
1270 glDrawBuffer(myDevice->offscreenBuffer);
1271 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1272 }
1273
1274 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1275 checkGLcall("glIntegerv");
1276 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1277 checkGLcall("glIntegerv");
1278 glPixelZoom(1.0, -1.0);
1279 checkGLcall("glPixelZoom");
1280
1281 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1282 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1283 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1284
1285 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1286 checkGLcall("glRasterPos2f");
1287
1288 /* Some drivers(radeon dri, others?) don't like exceptions during
1289 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1290 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1291 * catch to put the dib section in InSync mode, which leads to a crash
1292 * and a blocked x server on my radeon card.
1293 *
1294 * The following lines read the dib section so it is put in InSync mode
1295 * before glDrawPixels is called and the crash is prevented. There won't
1296 * be any interfering gdi accesses, because UnlockRect is called from
1297 * ReleaseDC, and the app won't use the dc any more afterwards.
1298 */
1299 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1300 volatile BYTE read;
1301 read = This->resource.allocatedMemory[0];
1302 }
1303
1304 if(This->Flags & SFLAG_PBO) {
1305 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1306 checkGLcall("glBindBufferARB");
1307 }
1308
1309 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1310 if(This->Flags & SFLAG_LOCKED) {
1311 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1312 (This->lockedRect.bottom - This->lockedRect.top)-1,
1313 fmt, type,
1314 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1315 checkGLcall("glDrawPixels");
1316 } else {
1317 glDrawPixels(This->currentDesc.Width,
1318 This->currentDesc.Height,
1319 fmt, type, mem);
1320 checkGLcall("glDrawPixels");
1321 }
1322
1323 if(This->Flags & SFLAG_PBO) {
1324 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1325 checkGLcall("glBindBufferARB");
1326 }
1327
1328 glPixelZoom(1.0,1.0);
1329 checkGLcall("glPixelZoom");
1330
1331 glRasterPos3iv(&prev_rasterpos[0]);
1332 checkGLcall("glRasterPos3iv");
1333
1334 /* Reset to previous pack row length */
1335 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1336 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1337
1338 if(!swapchain) {
1339 glDrawBuffer(myDevice->offscreenBuffer);
1340 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1341 } else if(swapchain->backBuffer) {
1342 glDrawBuffer(GL_BACK);
1343 checkGLcall("glDrawBuffer(GL_BACK)");
1344 } else {
1345 glDrawBuffer(GL_FRONT);
1346 checkGLcall("glDrawBuffer(GL_FRONT)");
1347 }
1348 LEAVE_GL();
1349
1350 return;
1351 }
1352
1353 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1354 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1355 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1356 IWineD3DSwapChainImpl *swapchain = NULL;
1357 BOOL fullsurface;
1358
1359 if (!(This->Flags & SFLAG_LOCKED)) {
1360 WARN("trying to Unlock an unlocked surf@%p\n", This);
1361 return WINEDDERR_NOTLOCKED;
1362 }
1363
1364 if (This->Flags & SFLAG_PBO) {
1365 TRACE("Freeing PBO memory\n");
1366 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1367 ENTER_GL();
1368 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1369 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1370 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1371 checkGLcall("glUnmapBufferARB");
1372 LEAVE_GL();
1373 This->resource.allocatedMemory = NULL;
1374 }
1375
1376 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1377
1378 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1379 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1380 goto unlock_end;
1381 }
1382
1383 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1384 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1385 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1386
1387 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1388 static BOOL warned = FALSE;
1389 if(!warned) {
1390 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1391 warned = TRUE;
1392 }
1393 goto unlock_end;
1394 }
1395
1396 if(This->dirtyRect.left == 0 &&
1397 This->dirtyRect.top == 0 &&
1398 This->dirtyRect.right == This->currentDesc.Width &&
1399 This->dirtyRect.bottom == This->currentDesc.Height) {
1400 fullsurface = TRUE;
1401 } else {
1402 /* TODO: Proper partial rectangle tracking */
1403 fullsurface = FALSE;
1404 This->Flags |= SFLAG_INSYSMEM;
1405 }
1406
1407 switch(wined3d_settings.rendertargetlock_mode) {
1408 case RTL_READTEX:
1409 case RTL_TEXTEX:
1410 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1411 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1412 /* drop through */
1413
1414 case RTL_AUTO:
1415 case RTL_READDRAW:
1416 case RTL_TEXDRAW:
1417 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1418 break;
1419 }
1420
1421 if(!fullsurface) {
1422 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1423 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1424 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1425 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1426 * not fully up to date because only a subrectangle was read in LockRect.
1427 */
1428 This->Flags &= ~SFLAG_INSYSMEM;
1429 This->Flags |= SFLAG_INDRAWABLE;
1430 }
1431
1432 This->dirtyRect.left = This->currentDesc.Width;
1433 This->dirtyRect.top = This->currentDesc.Height;
1434 This->dirtyRect.right = 0;
1435 This->dirtyRect.bottom = 0;
1436 } else if(iface == myDevice->stencilBufferTarget) {
1437 FIXME("Depth Stencil buffer locking is not implemented\n");
1438 } else {
1439 /* The rest should be a normal texture */
1440 IWineD3DBaseTextureImpl *impl;
1441 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1442 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1443 * states need resetting
1444 */
1445 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1446 if(impl->baseTexture.bindCount) {
1447 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1448 }
1449 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1450 }
1451 }
1452
1453 unlock_end:
1454 This->Flags &= ~SFLAG_LOCKED;
1455 memset(&This->lockedRect, 0, sizeof(RECT));
1456
1457 /* Overlays have to be redrawn manually after changes with the GL implementation */
1458 if(This->overlay_dest) {
1459 IWineD3DSurface_DrawOverlay(iface);
1460 }
1461 return WINED3D_OK;
1462 }
1463
1464 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1465 {
1466 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1467 WINED3DLOCKED_RECT lock;
1468 HRESULT hr;
1469 RGBQUAD col[256];
1470
1471 TRACE("(%p)->(%p)\n",This,pHDC);
1472
1473 if(This->Flags & SFLAG_USERPTR) {
1474 ERR("Not supported on surfaces with an application-provided surfaces\n");
1475 return WINEDDERR_NODC;
1476 }
1477
1478 /* Give more detailed info for ddraw */
1479 if (This->Flags & SFLAG_DCINUSE)
1480 return WINEDDERR_DCALREADYCREATED;
1481
1482 /* Can't GetDC if the surface is locked */
1483 if (This->Flags & SFLAG_LOCKED)
1484 return WINED3DERR_INVALIDCALL;
1485
1486 /* According to Direct3D9 docs, only these formats are supported */
1487 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1488 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1489 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1490 This->resource.format != WINED3DFMT_R8G8B8 &&
1491 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1492 }
1493
1494 memset(&lock, 0, sizeof(lock)); /* To be sure */
1495
1496 /* Create a DIB section if there isn't a hdc yet */
1497 if(!This->hDC) {
1498 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1499 if(This->Flags & SFLAG_CLIENT) {
1500 IWineD3DSurface_PreLoad(iface);
1501 }
1502
1503 /* Use the dib section from now on if we are not using a PBO */
1504 if(!(This->Flags & SFLAG_PBO))
1505 This->resource.allocatedMemory = This->dib.bitmap_data;
1506 }
1507
1508 /* Lock the surface */
1509 hr = IWineD3DSurface_LockRect(iface,
1510 &lock,
1511 NULL,
1512 0);
1513
1514 if(This->Flags & SFLAG_PBO) {
1515 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1516 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1517 }
1518
1519 if(FAILED(hr)) {
1520 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1521 /* keep the dib section */
1522 return hr;
1523 }
1524
1525 if(This->resource.format == WINED3DFMT_P8 ||
1526 This->resource.format == WINED3DFMT_A8P8) {
1527 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1528 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1529 unsigned int n;
1530 const PALETTEENTRY *pal = NULL;
1531
1532 if(This->palette) {
1533 pal = This->palette->palents;
1534 } else {
1535 IWineD3DSurfaceImpl *dds_primary;
1536 IWineD3DSwapChainImpl *swapchain;
1537 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1538 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1539 if (dds_primary && dds_primary->palette)
1540 pal = dds_primary->palette->palents;
1541 }
1542
1543 if (pal) {
1544 for (n=0; n<256; n++) {
1545 col[n].rgbRed = pal[n].peRed;
1546 col[n].rgbGreen = pal[n].peGreen;
1547 col[n].rgbBlue = pal[n].peBlue;
1548 col[n].rgbReserved = 0;
1549 }
1550 SetDIBColorTable(This->hDC, 0, 256, col);
1551 }
1552 }
1553
1554 *pHDC = This->hDC;
1555 TRACE("returning %p\n",*pHDC);
1556 This->Flags |= SFLAG_DCINUSE;
1557
1558 return WINED3D_OK;
1559 }
1560
1561 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1562 {
1563 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1564
1565 TRACE("(%p)->(%p)\n",This,hDC);
1566
1567 if (!(This->Flags & SFLAG_DCINUSE))
1568 return WINEDDERR_NODC;
1569
1570 if (This->hDC !=hDC) {
1571 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1572 return WINEDDERR_NODC;
1573 }
1574
1575 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1576 /* Copy the contents of the DIB over to the PBO */
1577 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1578 }
1579
1580 /* we locked first, so unlock now */
1581 IWineD3DSurface_UnlockRect(iface);
1582
1583 This->Flags &= ~SFLAG_DCINUSE;
1584
1585 return WINED3D_OK;
1586 }
1587
1588 /* ******************************************************
1589 IWineD3DSurface Internal (No mapping to directx api) parts follow
1590 ****************************************************** */
1591
1592 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) {
1593 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1594 const struct GlPixelFormatDesc *glDesc;
1595 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1596 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1597
1598 /* Default values: From the surface */
1599 *format = glDesc->glFormat;
1600 *type = glDesc->glType;
1601 *convert = NO_CONVERSION;
1602 *target_bpp = This->bytesPerPixel;
1603
1604 if(srgb_mode) {
1605 *internal = glDesc->glGammaInternal;
1606 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1607 *internal = glDesc->rtInternal;
1608 } else {
1609 *internal = glDesc->glInternal;
1610 }
1611
1612 /* Ok, now look if we have to do any conversion */
1613 switch(This->resource.format) {
1614 case WINED3DFMT_P8:
1615 /* ****************
1616 Paletted Texture
1617 **************** */
1618
1619 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1620 * of the two is available make sure texturing is requested as neither of the two works in
1621 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1622 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1623 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1624 * conflicts with this.
1625 */
1626 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1627 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1628 device->render_targets &&
1629 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1630 colorkey_active || !use_texturing ) {
1631 *format = GL_RGBA;
1632 *internal = GL_RGBA;
1633 *type = GL_UNSIGNED_BYTE;
1634 *target_bpp = 4;
1635 if(colorkey_active) {
1636 *convert = CONVERT_PALETTED_CK;
1637 } else {
1638 *convert = CONVERT_PALETTED;
1639 }
1640 }
1641 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1642 *format = GL_ALPHA;
1643 *internal = GL_RGBA;
1644 *type = GL_UNSIGNED_BYTE;
1645 *target_bpp = 1;
1646 }
1647
1648 break;
1649
1650 case WINED3DFMT_R3G3B2:
1651 /* **********************
1652 GL_UNSIGNED_BYTE_3_3_2
1653 ********************** */
1654 if (colorkey_active) {
1655 /* This texture format will never be used.. So do not care about color keying
1656 up until the point in time it will be needed :-) */
1657 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1658 }
1659 break;
1660
1661 case WINED3DFMT_R5G6B5:
1662 if (colorkey_active) {
1663 *convert = CONVERT_CK_565;
1664 *format = GL_RGBA;
1665 *internal = GL_RGBA;
1666 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1667 }
1668 break;
1669
1670 case WINED3DFMT_X1R5G5B5:
1671 if (colorkey_active) {
1672 *convert = CONVERT_CK_5551;
1673 *format = GL_BGRA;
1674 *internal = GL_RGBA;
1675 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1676 }
1677 break;
1678
1679 case WINED3DFMT_R8G8B8:
1680 if (colorkey_active) {
1681 *convert = CONVERT_CK_RGB24;
1682 *format = GL_RGBA;
1683 *internal = GL_RGBA;
1684 *type = GL_UNSIGNED_INT_8_8_8_8;
1685 *target_bpp = 4;
1686 }
1687 break;
1688
1689 case WINED3DFMT_X8R8G8B8:
1690 if (colorkey_active) {
1691 *convert = CONVERT_RGB32_888;
1692 *format = GL_RGBA;
1693 *internal = GL_RGBA;
1694 *type = GL_UNSIGNED_INT_8_8_8_8;
1695 }
1696 break;
1697
1698 case WINED3DFMT_V8U8:
1699 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1700 *convert = CONVERT_V8U8;
1701 *format = GL_BGR;
1702 *internal = GL_RGB8;
1703 *type = GL_UNSIGNED_BYTE;
1704 *target_bpp = 3;
1705 break;
1706
1707 case WINED3DFMT_L6V5U5:
1708 *convert = CONVERT_L6V5U5;
1709 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1710 *target_bpp = 3;
1711 /* Use format and types from table */
1712 } else {
1713 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1714 *target_bpp = 2;
1715 *format = GL_RGB;
1716 *internal = GL_RGB5;
1717 *type = GL_UNSIGNED_SHORT_5_6_5;
1718 }
1719 break;
1720
1721 case WINED3DFMT_X8L8V8U8:
1722 *convert = CONVERT_X8L8V8U8;
1723 *target_bpp = 4;
1724 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1725 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1726 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1727 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1728 * the needed type and format parameter, so the internal format contains a
1729 * 4th component, which is returned as alpha
1730 */
1731 } else {
1732 *format = GL_BGRA;
1733 *internal = GL_RGB8;
1734 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1735 }
1736 break;
1737
1738 case WINED3DFMT_Q8W8V8U8:
1739 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1740 *convert = CONVERT_Q8W8V8U8;
1741 *format = GL_BGRA;
1742 *internal = GL_RGBA8;
1743 *type = GL_UNSIGNED_BYTE;
1744 *target_bpp = 4;
1745 break;
1746
1747 case WINED3DFMT_V16U16:
1748 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1749 *convert = CONVERT_V16U16;
1750 *format = GL_BGR;
1751 *internal = GL_RGB16_EXT;
1752 *type = GL_UNSIGNED_SHORT;
1753 *target_bpp = 6;
1754 break;
1755
1756 case WINED3DFMT_A4L4:
1757 /* A4L4 exists as an internal gl format, but for some reason there is not
1758 * format+type combination to load it. Thus convert it to A8L8, then load it
1759 * with A4L4 internal, but A8L8 format+type
1760 */
1761 *convert = CONVERT_A4L4;
1762 *format = GL_LUMINANCE_ALPHA;
1763 *internal = GL_LUMINANCE4_ALPHA4;
1764 *type = GL_UNSIGNED_BYTE;
1765 *target_bpp = 2;
1766 break;
1767
1768 case WINED3DFMT_G16R16:
1769 *convert = CONVERT_G16R16;
1770 *format = GL_RGB;
1771 *internal = GL_RGB16_EXT;
1772 *type = GL_UNSIGNED_SHORT;
1773 *target_bpp = 6;
1774 break;
1775
1776 default:
1777 break;
1778 }
1779
1780 return WINED3D_OK;
1781 }
1782
1783 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1784 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1785 {
1786 const BYTE *source;
1787 BYTE *dest;
1788 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1789
1790 switch (convert) {
1791 case NO_CONVERSION:
1792 {
1793 memcpy(dst, src, pitch * height);
1794 break;
1795 }
1796 case CONVERT_PALETTED:
1797 case CONVERT_PALETTED_CK:
1798 {
1799 IWineD3DPaletteImpl* pal = This->palette;
1800 BYTE table[256][4];
1801 unsigned int x, y;
1802
1803 if( pal == NULL) {
1804 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1805 }
1806
1807 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1808
1809 for (y = 0; y < height; y++)
1810 {
1811 source = src + pitch * y;
1812 dest = dst + outpitch * y;
1813 /* This is an 1 bpp format, using the width here is fine */
1814 for (x = 0; x < width; x++) {
1815 BYTE color = *source++;
1816 *dest++ = table[color][0];
1817 *dest++ = table[color][1];
1818 *dest++ = table[color][2];
1819 *dest++ = table[color][3];
1820 }
1821 }
1822 }
1823 break;
1824
1825 case CONVERT_CK_565:
1826 {
1827 /* Converting the 565 format in 5551 packed to emulate color-keying.
1828
1829 Note : in all these conversion, it would be best to average the averaging
1830 pixels to get the color of the pixel that will be color-keyed to
1831 prevent 'color bleeding'. This will be done later on if ever it is
1832 too visible.
1833
1834 Note2: Nvidia documents say that their driver does not support alpha + color keying
1835 on the same surface and disables color keying in such a case
1836 */
1837 unsigned int x, y;
1838 const WORD *Source;
1839 WORD *Dest;
1840
1841 TRACE("Color keyed 565\n");
1842
1843 for (y = 0; y < height; y++) {
1844 Source = (const WORD *)(src + y * pitch);
1845 Dest = (WORD *) (dst + y * outpitch);
1846 for (x = 0; x < width; x++ ) {
1847 WORD color = *Source++;
1848 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1849 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1850 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1851 *Dest |= 0x0001;
1852 }
1853 Dest++;
1854 }
1855 }
1856 }
1857 break;
1858
1859 case CONVERT_CK_5551:
1860 {
1861 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1862 unsigned int x, y;
1863 const WORD *Source;
1864 WORD *Dest;
1865 TRACE("Color keyed 5551\n");
1866 for (y = 0; y < height; y++) {
1867 Source = (const WORD *)(src + y * pitch);
1868 Dest = (WORD *) (dst + y * outpitch);
1869 for (x = 0; x < width; x++ ) {
1870 WORD color = *Source++;
1871 *Dest = color;
1872 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1873 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1874 *Dest |= (1 << 15);
1875 }
1876 else {
1877 *Dest &= ~(1 << 15);
1878 }
1879 Dest++;
1880 }
1881 }
1882 }
1883 break;
1884
1885 case CONVERT_CK_RGB24:
1886 {
1887 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1888 unsigned int x, y;
1889 for (y = 0; y < height; y++)
1890 {
1891 source = src + pitch * y;
1892 dest = dst + outpitch * y;
1893 for (x = 0; x < width; x++) {
1894 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1895 DWORD dstcolor = color << 8;
1896 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1897 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1898 dstcolor |= 0xff;
1899 }
1900 *(DWORD*)dest = dstcolor;
1901 source += 3;
1902 dest += 4;
1903 }
1904 }
1905 }
1906 break;
1907
1908 case CONVERT_RGB32_888:
1909 {
1910 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1911 unsigned int x, y;
1912 for (y = 0; y < height; y++)
1913 {
1914 source = src + pitch * y;
1915 dest = dst + outpitch * y;
1916 for (x = 0; x < width; x++) {
1917 DWORD color = 0xffffff & *(const DWORD*)source;
1918 DWORD dstcolor = color << 8;
1919 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1920 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1921 dstcolor |= 0xff;
1922 }
1923 *(DWORD*)dest = dstcolor;
1924 source += 4;
1925 dest += 4;
1926 }
1927 }
1928 }
1929 break;
1930
1931 case CONVERT_V8U8:
1932 {
1933 unsigned int x, y;
1934 const short *Source;
1935 unsigned char *Dest;
1936 for(y = 0; y < height; y++) {
1937 Source = (const short *)(src + y * pitch);
1938 Dest = dst + y * outpitch;
1939 for (x = 0; x < width; x++ ) {
1940 long color = (*Source++);
1941 /* B */ Dest[0] = 0xff;
1942 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1943 /* R */ Dest[2] = (color) + 128; /* U */
1944 Dest += 3;
1945 }
1946 }
1947 break;
1948 }
1949
1950 case CONVERT_V16U16:
1951 {
1952 unsigned int x, y;
1953 const DWORD *Source;
1954 unsigned short *Dest;
1955 for(y = 0; y < height; y++) {
1956 Source = (const DWORD *)(src + y * pitch);
1957 Dest = (unsigned short *) (dst + y * outpitch);
1958 for (x = 0; x < width; x++ ) {
1959 DWORD color = (*Source++);
1960 /* B */ Dest[0] = 0xffff;
1961 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1962 /* R */ Dest[2] = (color ) + 32768; /* U */
1963 Dest += 3;
1964 }
1965 }
1966 break;
1967 }
1968
1969 case CONVERT_Q8W8V8U8:
1970 {
1971 unsigned int x, y;
1972 const DWORD *Source;
1973 unsigned char *Dest;
1974 for(y = 0; y < height; y++) {
1975 Source = (const DWORD *)(src + y * pitch);
1976 Dest = dst + y * outpitch;
1977 for (x = 0; x < width; x++ ) {
1978 long color = (*Source++);
1979 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1980 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1981 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1982 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1983 Dest += 4;
1984 }
1985 }
1986 break;
1987 }
1988
1989 case CONVERT_L6V5U5:
1990 {
1991 unsigned int x, y;
1992 const WORD *Source;
1993 unsigned char *Dest;
1994
1995 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1996 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1997 * fixed function and shaders without further conversion once the surface is
1998 * loaded
1999 */
2000 for(y = 0; y < height; y++) {
2001 Source = (const WORD *)(src + y * pitch);
2002 Dest = dst + y * outpitch;
2003 for (x = 0; x < width; x++ ) {
2004 short color = (*Source++);
2005 unsigned char l = ((color >> 10) & 0xfc);
2006 char v = ((color >> 5) & 0x3e);
2007 char u = ((color ) & 0x1f);
2008
2009 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2010 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2011 * shift. GL reads a signed value and converts it into an unsigned value.
2012 */
2013 /* M */ Dest[2] = l << 1;
2014
2015 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2016 * from 5 bit values to 8 bit values.
2017 */
2018 /* V */ Dest[1] = v << 3;
2019 /* U */ Dest[0] = u << 3;
2020 Dest += 3;
2021 }
2022 }
2023 } else {
2024 for(y = 0; y < height; y++) {
2025 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2026 Source = (const WORD *)(src + y * pitch);
2027 for (x = 0; x < width; x++ ) {
2028 short color = (*Source++);
2029 unsigned char l = ((color >> 10) & 0xfc);
2030 short v = ((color >> 5) & 0x3e);
2031 short u = ((color ) & 0x1f);
2032 short v_conv = v + 16;
2033 short u_conv = u + 16;
2034
2035 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2036 Dest_s += 1;
2037 }
2038 }
2039 }
2040 break;
2041 }
2042
2043 case CONVERT_X8L8V8U8:
2044 {
2045 unsigned int x, y;
2046 const DWORD *Source;
2047 unsigned char *Dest;
2048
2049 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2050 /* This implementation works with the fixed function pipeline and shaders
2051 * without further modification after converting the surface.
2052 */
2053 for(y = 0; y < height; y++) {
2054 Source = (const DWORD *)(src + y * pitch);
2055 Dest = dst + y * outpitch;
2056 for (x = 0; x < width; x++ ) {
2057 long color = (*Source++);
2058 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2059 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2060 /* U */ Dest[0] = (color & 0xff); /* U */
2061 /* I */ Dest[3] = 255; /* X */
2062 Dest += 4;
2063 }
2064 }
2065 } else {
2066 /* Doesn't work correctly with the fixed function pipeline, but can work in
2067 * shaders if the shader is adjusted. (There's no use for this format in gl's
2068 * standard fixed function pipeline anyway).
2069 */
2070 for(y = 0; y < height; y++) {
2071 Source = (const DWORD *)(src + y * pitch);
2072 Dest = dst + y * outpitch;
2073 for (x = 0; x < width; x++ ) {
2074 long color = (*Source++);
2075 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2076 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2077 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2078 Dest += 4;
2079 }
2080 }
2081 }
2082 break;
2083 }
2084
2085 case CONVERT_A4L4:
2086 {
2087 unsigned int x, y;
2088 const unsigned char *Source;
2089 unsigned char *Dest;
2090 for(y = 0; y < height; y++) {
2091 Source = src + y * pitch;
2092 Dest = dst + y * outpitch;
2093 for (x = 0; x < width; x++ ) {
2094 unsigned char color = (*Source++);
2095 /* A */ Dest[1] = (color & 0xf0) << 0;
2096 /* L */ Dest[0] = (color & 0x0f) << 4;
2097 Dest += 2;
2098 }
2099 }
2100 break;
2101 }
2102
2103 case CONVERT_G16R16:
2104 {
2105 unsigned int x, y;
2106 const WORD *Source;
2107 WORD *Dest;
2108
2109 for(y = 0; y < height; y++) {
2110 Source = (const WORD *)(src + y * pitch);
2111 Dest = (WORD *) (dst + y * outpitch);
2112 for (x = 0; x < width; x++ ) {
2113 WORD green = (*Source++);
2114 WORD red = (*Source++);
2115 Dest[0] = green;
2116 Dest[1] = red;
2117 Dest[2] = 0xffff;
2118 Dest += 3;
2119 }
2120 }
2121 break;
2122 }
2123
2124 default:
2125 ERR("Unsupported conversation type %d\n", convert);
2126 }
2127 return WINED3D_OK;
2128 }
2129
2130 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2131 IWineD3DPaletteImpl* pal = This->palette;
2132 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2133 BOOL index_in_alpha = FALSE;
2134 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2135 unsigned int i;
2136
2137 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2138 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2139 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2140 * duplicate entries. Store the color key in the unused alpha component to speed the
2141 * download up and to make conversion unneeded. */
2142 index_in_alpha = primary_render_target_is_p8(device);
2143
2144 if (pal == NULL) {
2145 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2146 if(dxVersion <= 7) {
2147 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2148 if(index_in_alpha) {
2149 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2150 there's no palette at this time. */
2151 for (i = 0; i < 256; i++) table[i][3] = i;
2152 }
2153 } else {
2154 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2155 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2156 capability flag is present (wine does advertise this capability) */
2157 for (i = 0; i < 256; i++) {
2158 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2159 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2160 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2161 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2162 }
2163 }
2164 } else {
2165 TRACE("Using surface palette %p\n", pal);
2166 /* Get the surface's palette */
2167 for (i = 0; i < 256; i++) {
2168 table[i][0] = pal->palents[i].peRed;
2169 table[i][1] = pal->palents[i].peGreen;
2170 table[i][2] = pal->palents[i].peBlue;
2171
2172 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2173 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2174 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2175 of pixels that should be masked away is set to 0. */
2176 if(index_in_alpha) {
2177 table[i][3] = i;
2178 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2179 table[i][3] = 0x00;
2180 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2181 table[i][3] = pal->palents[i].peFlags;
2182 } else {
2183 table[i][3] = 0xFF;
2184 }
2185 }
2186 }
2187 }
2188
2189 /* This function is used in case of 8bit paletted textures to upload the palette.
2190 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2191 extensions like ATI_fragment_shaders is possible.
2192 */
2193 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2194 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2195 BYTE table[256][4];
2196 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2197
2198 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2199
2200 /* Try to use the paletted texture extension */
2201 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2202 {
2203 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2204 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2205 }
2206 else
2207 {
2208 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2209 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2210 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2211
2212 /* Create the fragment program if we don't have it */
2213 if(!device->paletteConversionShader)
2214 {
2215 const char *fragment_palette_conversion =
2216 "!!ARBfp1.0\n"
2217 "TEMP index;\n"
2218 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2219 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2220 /* The alpha-component contains the palette index */
2221 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2222 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2223 "MAD index.a, index.a, constants.x, constants.y;\n"
2224 /* Use the alpha-component as an index in the palette to get the final color */
2225 "TEX result.color, index.a, texture[1], 1D;\n"
2226 "END";
2227
2228 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2229 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2230 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2231 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2232 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2233 }
2234
2235 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2236 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2237
2238 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2239 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2240
2241 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2242 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2243 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2244 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2245
2246 /* Switch back to unit 0 in which the 2D texture will be stored. */
2247 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2248
2249 /* Rebind the texture because it isn't bound anymore */
2250 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2251 }
2252 }
2253
2254 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2255 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2256
2257 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2258 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2259 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2260 */
2261 return FALSE;
2262 }
2263
2264 if(This->palette9) {
2265 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2266 return FALSE;
2267 }
2268 } else {
2269 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2270 }
2271 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2272 return TRUE;
2273 }
2274
2275 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2276 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2277
2278 if (!(This->Flags & SFLAG_INTEXTURE)) {
2279 TRACE("Reloading because surface is dirty\n");
2280 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2281 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2282 /* Reload: vice versa OR */
2283 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2284 /* Also reload: Color key is active AND the color key has changed */
2285 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2286 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2287 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2288 TRACE("Reloading because of color keying\n");
2289 /* To perform the color key conversion we need a sysmem copy of
2290 * the surface. Make sure we have it
2291 */
2292
2293 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2294 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2295 /* TODO: This is not necessarily needed with hw palettized texture support */
2296 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2297 } else {
2298 TRACE("surface is already in texture\n");
2299 return WINED3D_OK;
2300 }
2301
2302 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2303 * These resources are not bound by device size or format restrictions. Because of this,
2304 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2305 * However, these resources can always be created, locked, and copied.
2306 */
2307 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2308 {
2309 FIXME("(%p) Operation not supported for scratch textures\n",This);
2310 return WINED3DERR_INVALIDCALL;
2311 }
2312
2313 This->srgb = srgb_mode;
2314 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2315
2316 #if 0
2317 {
2318 static unsigned int gen = 0;
2319 char buffer[4096];
2320 ++gen;
2321 if ((gen % 10) == 0) {
2322 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2323 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2324 }
2325 /*
2326 * debugging crash code
2327 if (gen == 250) {
2328 void** test = NULL;
2329 *test = 0;
2330 }
2331 */
2332 }
2333 #endif
2334
2335 if (!(This->Flags & SFLAG_DONOTFREE)) {
2336 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2337 This->resource.allocatedMemory = NULL;
2338 This->resource.heapMemory = NULL;
2339 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2340 }
2341
2342 return WINED3D_OK;
2343 }
2344
2345 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2346 /* TODO: check for locks */
2347 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2348 IWineD3DBaseTexture *baseTexture = NULL;
2349 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2350
2351 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2352 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2353 TRACE("Passing to container\n");
2354 IWineD3DBaseTexture_BindTexture(baseTexture);
2355 IWineD3DBaseTexture_Release(baseTexture);
2356 } else {
2357 TRACE("(%p) : Binding surface\n", This);
2358
2359 if(!device->isInDraw) {
2360 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2361 }
2362
2363 ENTER_GL();
2364
2365 if (!This->glDescription.level) {
2366 if (!This->glDescription.textureName) {
2367 glGenTextures(1, &This->glDescription.textureName);
2368 checkGLcall("glGenTextures");
2369 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
2370
2371 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2372 checkGLcall("glBindTexture");
2373 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2374 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2375 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2376 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2377 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2378 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2379 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2380 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2381 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2382 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2383 }
2384 /* This is where we should be reducing the amount of GLMemoryUsed */
2385 } else if (This->glDescription.textureName) {
2386 /* Mipmap surfaces should have a base texture container */
2387 ERR("Mipmap surface has a glTexture bound to it!\n");
2388 }
2389
2390 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2391 checkGLcall("glBindTexture");
2392
2393 LEAVE_GL();
2394 }
2395 return;
2396 }
2397
2398 #include <errno.h>
2399 #include <stdio.h>
2400 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2401 {
2402 FILE* f = NULL;
2403 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2404 char *allocatedMemory;
2405 const char *textureRow;
2406 IWineD3DSwapChain *swapChain = NULL;
2407 int width, height, i, y;
2408 GLuint tmpTexture = 0;
2409 DWORD color;
2410 /*FIXME:
2411 Textures may not be stored in ->allocatedgMemory and a GlTexture
2412 so we should lock the surface before saving a snapshot, or at least check that
2413 */
2414 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2415 by calling GetTexImage and in compressed form by calling
2416 GetCompressedTexImageARB. Queried compressed images can be saved and
2417 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2418 texture images do not need to be processed by the GL and should
2419 significantly improve texture loading performance relative to uncompressed
2420 images. */
2421
2422 /* Setup the width and height to be the internal texture width and height. */
2423 width = This->pow2Width;
2424 height = This->pow2Height;
2425 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2426 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2427
2428 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2429 /* if were not a real texture then read the back buffer into a real texture */
2430 /* we don't want to interfere with the back buffer so read the data into a temporary
2431 * texture and then save the data out of the temporary texture
2432 */
2433 GLint prevRead;
2434 ENTER_GL();
2435 TRACE("(%p) Reading render target into texture\n", This);
2436
2437 glGenTextures(1, &tmpTexture);
2438 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2439
2440 glTexImage2D(GL_TEXTURE_2D,
2441 0,
2442 GL_RGBA,
2443 width,
2444 height,
2445 0/*border*/,
2446 GL_RGBA,
2447 GL_UNSIGNED_INT_8_8_8_8_REV,
2448 NULL);
2449
2450 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2451 checkGLcall("glGetIntegerv");
2452 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2453 checkGLcall("glReadBuffer");
2454 glCopyTexImage2D(GL_TEXTURE_2D,
2455 0,
2456 GL_RGBA,
2457 0,
2458 0,
2459 width,
2460 height,
2461 0);
2462
2463 checkGLcall("glCopyTexImage2D");
2464 glReadBuffer(prevRead);
2465 LEAVE_GL();
2466
2467 } else { /* bind the real texture, and make sure it up to date */
2468 IWineD3DSurface_PreLoad(iface);
2469 surface_bind_and_dirtify(This);
2470 }
2471 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2472 ENTER_GL();
2473 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2474 glGetTexImage(GL_TEXTURE_2D,
2475 This->glDescription.level,
2476 GL_RGBA,
2477 GL_UNSIGNED_INT_8_8_8_8_REV,
2478 allocatedMemory);
2479 checkGLcall("glTexImage2D");
2480 if (tmpTexture) {
2481 glBindTexture(GL_TEXTURE_2D, 0);
2482 glDeleteTextures(1, &tmpTexture);
2483 }
2484 LEAVE_GL();
2485
2486 f = fopen(filename, "w+");
2487 if (NULL == f) {
2488 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2489 return WINED3DERR_INVALIDCALL;
2490 }
2491 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2492 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2493 /* TGA header */
2494 fputc(0,f);
2495 fputc(0,f);
2496 fputc(2,f);
2497 fputc(0,f);
2498 fputc(0,f);
2499 fputc(0,f);
2500 fputc(0,f);
2501 fputc(0,f);
2502 fputc(0,f);
2503 fputc(0,f);
2504 fputc(0,f);
2505 fputc(0,f);
2506 /* short width*/
2507 fwrite(&width,2,1,f);
2508 /* short height */
2509 fwrite(&height,2,1,f);
2510 /* format rgba */
2511 fputc(0x20,f);
2512 fputc(0x28,f);
2513 /* raw data */
2514 /* 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 */
2515 if(swapChain)
2516 textureRow = allocatedMemory + (width * (height - 1) *4);
2517 else
2518 textureRow = allocatedMemory;
2519 for (y = 0 ; y < height; y++) {
2520 for (i = 0; i < width; i++) {
2521 color = *((const DWORD*)textureRow);
2522 fputc((color >> 16) & 0xFF, f); /* B */
2523 fputc((color >> 8) & 0xFF, f); /* G */
2524 fputc((color >> 0) & 0xFF, f); /* R */
2525 fputc((color >> 24) & 0xFF, f); /* A */
2526 textureRow += 4;
2527 }
2528 /* take two rows of the pointer to the texture memory */
2529 if(swapChain)
2530 (textureRow-= width << 3);
2531
2532 }
2533 TRACE("Closing file\n");
2534 fclose(f);
2535
2536 if(swapChain) {
2537 IWineD3DSwapChain_Release(swapChain);
2538 }
2539 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2540 return WINED3D_OK;
2541 }
2542
2543 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2544 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2545 HRESULT hr;
2546 const struct GlPixelFormatDesc *glDesc;
2547 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2548
2549 TRACE("(%p) : Calling base function first\n", This);
2550 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2551 if(SUCCEEDED(hr)) {
2552 /* Setup some glformat defaults */
2553 This->glDescription.glFormat = glDesc->glFormat;
2554 This->glDescription.glFormatInternal = glDesc->glInternal;
2555 This->glDescription.glType = glDesc->glType;
2556
2557 This->Flags &= ~SFLAG_ALLOCATED;
2558 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2559 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2560 }
2561 return hr;
2562 }
2563
2564 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2565 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2566
2567 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2568 WARN("Surface is locked or the HDC is in use\n");
2569 return WINED3DERR_INVALIDCALL;
2570 }
2571
2572 if(Mem && Mem != This->resource.allocatedMemory) {
2573 void *release = NULL;
2574
2575 /* Do I have to copy the old surface content? */
2576 if(This->Flags & SFLAG_DIBSECTION) {
2577 /* Release the DC. No need to hold the critical section for the update
2578 * Thread because this thread runs only on front buffers, but this method
2579 * fails for render targets in the check above.
2580 */
2581 SelectObject(This->hDC, This->dib.holdbitmap);
2582 DeleteDC(This->hDC);
2583 /* Release the DIB section */
2584 DeleteObject(This->dib.DIBsection);
2585 This->dib.bitmap_data = NULL;
2586 This->resource.allocatedMemory = NULL;
2587 This->hDC = NULL;
2588 This->Flags &= ~SFLAG_DIBSECTION;
2589 } else if(!(This->Flags & SFLAG_USERPTR)) {
2590 release = This->resource.heapMemory;
2591 This->resource.heapMemory = NULL;
2592 }
2593 This->resource.allocatedMemory = Mem;
2594 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2595
2596 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2597 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2598
2599 /* For client textures opengl has to be notified */
2600 if(This->Flags & SFLAG_CLIENT) {
2601 This->Flags &= ~SFLAG_ALLOCATED;
2602 IWineD3DSurface_PreLoad(iface);
2603 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2604 }
2605
2606 /* Now free the old memory if any */
2607 HeapFree(GetProcessHeap(), 0, release);
2608 } else if(This->Flags & SFLAG_USERPTR) {
2609 /* LockRect and GetDC will re-create the dib section and allocated memory */
2610 This->resource.allocatedMemory = NULL;
2611 /* HeapMemory should be NULL already */
2612 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2613 This->Flags &= ~SFLAG_USERPTR;
2614
2615 if(This->Flags & SFLAG_CLIENT) {
2616 This->Flags &= ~SFLAG_ALLOCATED;
2617 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2618 IWineD3DSurface_PreLoad(iface);
2619 }
2620 }
2621 return WINED3D_OK;
2622 }
2623
2624 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2625
2626 /* Flip the surface contents */
2627 /* Flip the DC */
2628 {
2629 HDC tmp;
2630 tmp = front->hDC;
2631 front->hDC = back->hDC;
2632 back->hDC = tmp;
2633 }
2634
2635 /* Flip the DIBsection */
2636 {
2637 HBITMAP tmp;
2638 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2639 tmp = front->dib.DIBsection;
2640 front->dib.DIBsection = back->dib.DIBsection;
2641 back->dib.DIBsection = tmp;
2642
2643 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2644 else front->Flags &= ~SFLAG_DIBSECTION;
2645 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2646 else back->Flags &= ~SFLAG_DIBSECTION;
2647 }
2648
2649 /* Flip the surface data */
2650 {
2651 void* tmp;
2652
2653 tmp = front->dib.bitmap_data;
2654 front->dib.bitmap_data = back->dib.bitmap_data;
2655 back->dib.bitmap_data = tmp;
2656
2657 tmp = front->resource.allocatedMemory;
2658 front->resource.allocatedMemory = back->resource.allocatedMemory;
2659 back->resource.allocatedMemory = tmp;
2660
2661 tmp = front->resource.heapMemory;
2662 front->resource.heapMemory = back->resource.heapMemory;
2663 back->resource.heapMemory = tmp;
2664 }
2665
2666 /* Flip the PBO */
2667 {
2668 GLuint tmp_pbo = front->pbo;
2669 front->pbo = back->pbo;
2670 back->pbo = tmp_pbo;
2671 }
2672
2673 /* client_memory should not be different, but just in case */
2674 {
2675 BOOL tmp;
2676 tmp = front->dib.client_memory;
2677 front->dib.client_memory = back->dib.client_memory;
2678 back->dib.client_memory = tmp;
2679 }
2680
2681 /* Flip the opengl texture */
2682 {
2683 glDescriptor tmp_desc = back->glDescription;
2684 back->glDescription = front->glDescription;
2685 front->glDescription = tmp_desc;
2686 }
2687
2688 {
2689 DWORD tmp_flags = back->Flags;
2690 back->Flags = front->Flags;
2691 front->Flags = tmp_flags;
2692 }
2693 }
2694
2695 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2696 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2697 IWineD3DSwapChainImpl *swapchain = NULL;
2698 HRESULT hr;
2699 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2700
2701 /* Flipping is only supported on RenderTargets and overlays*/
2702 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2703 WARN("Tried to flip a non-render target, non-overlay surface\n");
2704 return WINEDDERR_NOTFLIPPABLE;
2705 }
2706
2707 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2708 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2709
2710 /* Update the overlay if it is visible */
2711 if(This->overlay_dest) {
2712 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2713 } else {
2714 return WINED3D_OK;
2715 }
2716 }
2717
2718 if(override) {
2719 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2720 * FIXME("(%p) Target override is not supported by now\n", This);
2721 * Additionally, it isn't really possible to support triple-buffering
2722 * properly on opengl at all
2723 */
2724 }
2725
2726 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2727 if(!swapchain) {
2728 ERR("Flipped surface is not on a swapchain\n");
2729 return WINEDDERR_NOTFLIPPABLE;
2730 }
2731
2732 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2733 * and only d3d8 and d3d9 apps specify the presentation interval
2734 */
2735 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2736 /* Most common case first to avoid wasting time on all the other cases */
2737 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2738 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2739 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2740 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2741 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2742 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2743 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2744 } else {
2745 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2746 }
2747
2748 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2749 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2750 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2751 return hr;
2752 }
2753
2754 /* Does a direct frame buffer -> texture copy. Stretching is done
2755 * with single pixel copy calls
2756 */
2757 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2758 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2759 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2760 {
2761 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2762 float xrel, yrel;
2763 UINT row;
2764 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2765
2766
2767 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2768 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2769 ENTER_GL();
2770
2771 /* Bind the target texture */
2772 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2773 checkGLcall("glBindTexture");
2774 if(!swapchain) {
2775 TRACE("Reading from an offscreen target\n");
2776 upsidedown = !upsidedown;
2777 glReadBuffer(myDevice->offscreenBuffer);
2778 } else {
2779 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2780 glReadBuffer(buffer);
2781 }
2782 checkGLcall("glReadBuffer");
2783
2784 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2785 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2786
2787 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2788 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2789
2790 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2791 ERR("Texture filtering not supported in direct blit\n");
2792 }
2793 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2794 ERR("Texture filtering not supported in direct blit\n");
2795 }
2796
2797 if(upsidedown &&
2798 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2799 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2800 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2801
2802 glCopyTexSubImage2D(This->glDescription.target,
2803 This->glDescription.level,
2804 drect->x1, drect->y1, /* xoffset, yoffset */
2805 srect->x1, Src->currentDesc.Height - srect->y2,
2806 drect->x2 - drect->x1, drect->y2 - drect->y1);
2807 } else {
2808 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2809 /* I have to process this row by row to swap the image,
2810 * otherwise it would be upside down, so stretching in y direction
2811 * doesn't cost extra time
2812 *
2813 * However, stretching in x direction can be avoided if not necessary
2814 */
2815 for(row = drect->y1; row < drect->y2; row++) {
2816 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2817 /* Well, that stuff works, but it's very slow.
2818 * find a better way instead
2819 */
2820 UINT col;
2821
2822 for(col = drect->x1; col < drect->x2; col++) {
2823 glCopyTexSubImage2D(This->glDescription.target,
2824 This->glDescription.level,
2825 drect->x1 + col, row, /* xoffset, yoffset */
2826 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2827 1, 1);
2828 }
2829 } else {
2830 glCopyTexSubImage2D(This->glDescription.target,
2831 This->glDescription.level,
2832 drect->x1, row, /* xoffset, yoffset */
2833 srect->x1, yoffset - (int) (row * yrel),
2834 drect->x2-drect->x1, 1);
2835 }
2836 }
2837 }
2838 checkGLcall("glCopyTexSubImage2D");
2839
2840 LEAVE_GL();
2841 }
2842
2843 /* Uses the hardware to stretch and flip the image */
2844 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2845 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2846 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2847 {
2848 GLuint src, backup = 0;
2849 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2850 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2851 float left, right, top, bottom; /* Texture coordinates */
2852 UINT fbwidth = Src->currentDesc.Width;
2853 UINT fbheight = Src->currentDesc.Height;
2854 GLenum drawBuffer = GL_BACK;
2855 GLenum texture_target;
2856 BOOL noBackBufferBackup;
2857
2858 TRACE("Using hwstretch blit\n");
2859 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2860 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2861 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2862
2863 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2864 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2865 /* Get it a description */
2866 IWineD3DSurface_PreLoad(SrcSurface);
2867 }
2868 ENTER_GL();
2869
2870 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2871 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2872 */
2873 if(myDevice->activeContext->aux_buffers >= 2) {
2874 /* Got more than one aux buffer? Use the 2nd aux buffer */
2875 drawBuffer = GL_AUX1;
2876 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2877 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2878 drawBuffer = GL_AUX0;
2879 }
2880
2881 if(noBackBufferBackup) {
2882 glGenTextures(1, &backup);
2883 checkGLcall("glGenTextures\n");
2884 glBindTexture(GL_TEXTURE_2D, backup);
2885 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2886 texture_target = GL_TEXTURE_2D;
2887 } else {
2888 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2889 * we are reading from the back buffer, the backup can be used as source texture
2890 */
2891 texture_target = Src->glDescription.target;
2892 glBindTexture(texture_target, Src->glDescription.textureName);
2893 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2894 glEnable(texture_target);
2895 checkGLcall("glEnable(texture_target)");
2896
2897 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2898 Src->Flags &= ~SFLAG_INTEXTURE;
2899 }
2900
2901 if(swapchain) {
2902 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2903 } else {
2904 TRACE("Reading from an offscreen target\n");
2905 upsidedown = !upsidedown;
2906 glReadBuffer(myDevice->offscreenBuffer);
2907 }
2908
2909 /* TODO: Only back up the part that will be overwritten */
2910 glCopyTexSubImage2D(texture_target, 0,
2911 0, 0 /* read offsets */,
2912 0, 0,
2913 fbwidth,
2914 fbheight);
2915
2916 checkGLcall("glCopyTexSubImage2D");
2917
2918 /* No issue with overriding these - the sampler is dirty due to blit usage */
2919 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2920 magLookup[Filter - WINED3DTEXF_NONE]);
2921 checkGLcall("glTexParameteri");
2922 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2923 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
2924 checkGLcall("glTexParameteri");
2925
2926 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2927 src = backup ? backup : Src->glDescription.textureName;
2928 } else {
2929 glReadBuffer(GL_FRONT);
2930 checkGLcall("glReadBuffer(GL_FRONT)");
2931
2932 glGenTextures(1, &src);
2933 checkGLcall("glGenTextures(1, &src)");
2934 glBindTexture(GL_TEXTURE_2D, src);
2935 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2936
2937 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2938 * out for power of 2 sizes
2939 */
2940 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2941 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2942 checkGLcall("glTexImage2D");
2943 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2944 0, 0 /* read offsets */,
2945 0, 0,
2946 fbwidth,
2947 fbheight);
2948
2949 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2950 checkGLcall("glTexParameteri");
2951 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2952 checkGLcall("glTexParameteri");
2953
2954 glReadBuffer(GL_BACK);
2955 checkGLcall("glReadBuffer(GL_BACK)");
2956
2957 if(texture_target != GL_TEXTURE_2D) {
2958 glDisable(texture_target);
2959 glEnable(GL_TEXTURE_2D);
2960 texture_target = GL_TEXTURE_2D;
2961 }
2962 }
2963 checkGLcall("glEnd and previous");
2964
2965 left = srect->x1;
2966 right = srect->x2;
2967
2968 if(upsidedown) {
2969 top = Src->currentDesc.Height - srect->y1;
2970 bottom = Src->currentDesc.Height - srect->y2;
2971 } else {
2972 top = Src->currentDesc.Height - srect->y2;
2973 bottom = Src->currentDesc.Height - srect->y1;
2974 }
2975
2976 if(Src->Flags & SFLAG_NORMCOORD) {
2977 left /= Src->pow2Width;
2978 right /= Src->pow2Width;
2979 top /= Src->pow2Height;
2980 bottom /= Src->pow2Height;
2981 }
2982
2983 /* draw the source texture stretched and upside down. The correct surface is bound already */
2984 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2985 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2986
2987 glDrawBuffer(drawBuffer);
2988 glReadBuffer(drawBuffer);
2989
2990 glBegin(GL_QUADS);
2991 /* bottom left */
2992 glTexCoord2f(left, bottom);
2993 glVertex2i(0, fbheight);
2994
2995 /* top left */
2996 glTexCoord2f(left, top);
2997 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2998
2999 /* top right */
3000 glTexCoord2f(right, top);
3001 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3002
3003 /* bottom right */
3004 glTexCoord2f(right, bottom);
3005 glVertex2i(drect->x2 - drect->x1, fbheight);
3006 glEnd();
3007 checkGLcall("glEnd and previous");
3008
3009 if(texture_target != This->glDescription.target) {
3010 glDisable(texture_target);
3011 glEnable(This->glDescription.target);
3012 texture_target = This->glDescription.target;
3013 }
3014
3015 /* Now read the stretched and upside down image into the destination texture */
3016 glBindTexture(texture_target, This->glDescription.textureName);
3017 checkGLcall("glBindTexture");
3018 glCopyTexSubImage2D(texture_target,
3019 0,
3020 drect->x1, drect->y1, /* xoffset, yoffset */
3021 0, 0, /* We blitted the image to the origin */
3022 drect->x2 - drect->x1, drect->y2 - drect->y1);
3023 checkGLcall("glCopyTexSubImage2D");
3024
3025 if(drawBuffer == GL_BACK) {
3026 /* Write the back buffer backup back */
3027 if(backup) {
3028 if(texture_target != GL_TEXTURE_2D) {
3029 glDisable(texture_target);
3030 glEnable(GL_TEXTURE_2D);
3031 texture_target = GL_TEXTURE_2D;
3032 }
3033 glBindTexture(GL_TEXTURE_2D, backup);
3034 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3035 } else {
3036 if(texture_target != Src->glDescription.target) {
3037 glDisable(texture_target);
3038 glEnable(Src->glDescription.target);
3039 texture_target = Src->glDescription.target;
3040 }
3041 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3042 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3043 }
3044
3045 glBegin(GL_QUADS);
3046 /* top left */
3047 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3048 glVertex2i(0, 0);
3049
3050 /* bottom left */
3051 glTexCoord2f(0.0, 0.0);
3052 glVertex2i(0, fbheight);
3053
3054 /* bottom right */
3055 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3056 glVertex2i(fbwidth, Src->currentDesc.Height);
3057
3058 /* top right */
3059 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3060 glVertex2i(fbwidth, 0);
3061 glEnd();
3062 } else {
3063 /* Restore the old draw buffer */
3064 glDrawBuffer(GL_BACK);
3065 }
3066 glDisable(texture_target);
3067 checkGLcall("glDisable(texture_target)");
3068
3069 /* Cleanup */
3070 if(src != Src->glDescription.textureName && src != backup) {
3071 glDeleteTextures(1, &src);
3072 checkGLcall("glDeleteTextures(1, &src)");
3073 }
3074 if(backup) {
3075 glDeleteTextures(1, &backup);
3076 checkGLcall("glDeleteTextures(1, &backup)");
3077 }
3078
3079 LEAVE_GL();
3080 }
3081
3082 /* Not called from the VTable */
3083 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3084 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3085 WINED3DTEXTUREFILTERTYPE Filter)
3086 {
3087 WINED3DRECT rect;
3088 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3089 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3090 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3091
3092 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3093
3094 /* Get the swapchain. One of the surfaces has to be a primary surface */
3095 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3096 WARN("Destination is in sysmem, rejecting gl blt\n");
3097 return WINED3DERR_INVALIDCALL;
3098 }
3099 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3100 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3101 if(Src) {
3102 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3103 WARN("Src is in sysmem, rejecting gl blt\n");
3104 return WINED3DERR_INVALIDCALL;
3105 }
3106 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3107 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3108 }
3109
3110 /* Early sort out of cases where no render target is used */
3111 if(!dstSwapchain && !srcSwapchain &&
3112 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3113 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3114 return WINED3DERR_INVALIDCALL;
3115 }
3116
3117 /* No destination color keying supported */
3118 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3119 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3120 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3121 return WINED3DERR_INVALIDCALL;
3122 }
3123
3124 if (DestRect) {
3125 rect.x1 = DestRect->left;
3126 rect.y1 = DestRect->top;
3127 rect.x2 = DestRect->right;
3128 rect.y2 = DestRect->bottom;
3129 } else {
3130 rect.x1 = 0;
3131 rect.y1 = 0;
3132 rect.x2 = This->currentDesc.Width;
3133 rect.y2 = This->currentDesc.Height;
3134 }
3135
3136 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3137 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3138 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3139 /* Half-life does a Blt from the back buffer to the front buffer,
3140 * Full surface size, no flags... Use present instead
3141 *
3142 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3143 */
3144
3145 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3146 while(1)
3147 {
3148 RECT mySrcRect;
3149 TRACE("Looking if a Present can be done...\n");
3150 /* Source Rectangle must be full surface */
3151 if( SrcRect ) {
3152 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3153 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3154 TRACE("No, Source rectangle doesn't match\n");
3155 break;
3156 }
3157 }
3158 mySrcRect.left = 0;
3159 mySrcRect.top = 0;
3160 mySrcRect.right = Src->currentDesc.Width;
3161 mySrcRect.bottom = Src->currentDesc.Height;
3162
3163 /* No stretching may occur */
3164 if(mySrcRect.right != rect.x2 - rect.x1 ||
3165 mySrcRect.bottom != rect.y2 - rect.y1) {
3166 TRACE("No, stretching is done\n");
3167 break;
3168 }
3169
3170 /* Destination must be full surface or match the clipping rectangle */
3171 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3172 {
3173 RECT cliprect;
3174 POINT pos[2];
3175 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3176 pos[0].x = rect.x1;
3177 pos[0].y = rect.y1;
3178 pos[1].x = rect.x2;
3179 pos[1].y = rect.y2;
3180 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3181 pos, 2);
3182
3183 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3184 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3185 {
3186 TRACE("No, dest rectangle doesn't match(clipper)\n");
3187 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3188 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3189 break;
3190 }
3191 }
3192 else
3193 {
3194 if(rect.x1 != 0 || rect.y1 != 0 ||
3195 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3196 TRACE("No, dest rectangle doesn't match(surface size)\n");
3197 break;
3198 }
3199 }
3200
3201 TRACE("Yes\n");
3202
3203 /* These flags are unimportant for the flag check, remove them */
3204 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3205 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3206
3207 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3208 * take very long, while a flip is fast.
3209 * This applies to Half-Life, which does such Blts every time it finished
3210 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3211 * menu. This is also used by all apps when they do windowed rendering
3212 *
3213 * The problem is that flipping is not really the same as copying. After a
3214 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3215 * untouched. Therefore it's necessary to override the swap effect
3216 * and to set it back after the flip.
3217 *
3218 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3219 * testcases.
3220 */
3221
3222 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3223 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3224
3225 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3226 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3227
3228 dstSwapchain->presentParms.SwapEffect = orig_swap;
3229
3230 return WINED3D_OK;
3231 }
3232 break;
3233 }
3234
3235 TRACE("Unsupported blit between buffers on the same swapchain\n");
3236 return WINED3DERR_INVALIDCALL;
3237 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3238 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3239 return WINED3DERR_INVALIDCALL;
3240 } else if(dstSwapchain && srcSwapchain) {
3241 FIXME("Implement hardware blit between two different swapchains\n");
3242 return WINED3DERR_INVALIDCALL;
3243 } else if(dstSwapchain) {
3244 if(SrcSurface == myDevice->render_targets[0]) {
3245 TRACE("Blit from active render target to a swapchain\n");
3246 /* Handled with regular texture -> swapchain blit */
3247 }
3248 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3249 FIXME("Implement blit from a swapchain to the active render target\n");
3250 return WINED3DERR_INVALIDCALL;
3251 }
3252
3253 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3254 /* Blit from render target to texture */
3255 WINED3DRECT srect;
3256 BOOL upsideDown, stretchx;
3257 BOOL paletteOverride = FALSE;
3258
3259 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3260 TRACE("Color keying not supported by frame buffer to texture blit\n");
3261 return WINED3DERR_INVALIDCALL;
3262 /* Destination color key is checked above */
3263 }
3264
3265 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3266 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3267 */
3268 if(SrcRect) {
3269 if(SrcRect->top < SrcRect->bottom) {
3270 srect.y1 = SrcRect->top;
3271 srect.y2 = SrcRect->bottom;
3272 upsideDown = FALSE;
3273 } else {
3274 srect.y1 = SrcRect->bottom;
3275 srect.y2 = SrcRect->top;
3276 upsideDown = TRUE;
3277 }
3278 srect.x1 = SrcRect->left;
3279 srect.x2 = SrcRect->right;
3280 } else {
3281 srect.x1 = 0;
3282 srect.y1 = 0;
3283 srect.x2 = Src->currentDesc.Width;
3284 srect.y2 = Src->currentDesc.Height;
3285 upsideDown = FALSE;
3286 }
3287 if(rect.x1 > rect.x2) {
3288 UINT tmp = rect.x2;
3289 rect.x2 = rect.x1;
3290 rect.x1 = tmp;
3291 upsideDown = !upsideDown;
3292 }
3293
3294 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3295 stretchx = TRUE;
3296 } else {
3297 stretchx = FALSE;
3298 }
3299
3300 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3301 * In this case grab the palette from the render target. */
3302 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3303 paletteOverride = TRUE;
3304 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3305 This->palette = Src->palette;
3306 }
3307
3308 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3309 * flip the image nor scale it.
3310 *
3311 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3312 * -> If the app wants a image width an unscaled width, copy it line per line
3313 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3314 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3315 * back buffer. This is slower than reading line per line, thus not used for flipping
3316 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3317 * pixel by pixel
3318 *
3319 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3320 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3321 * backends.
3322 */
3323 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3324 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3325 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3326 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3327 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3328 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3329 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3330 } else {
3331 TRACE("Using hardware stretching to flip / stretch the texture\n");
3332 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3333 }
3334
3335 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3336 if(paletteOverride)
3337 This->palette = NULL;
3338
3339 if(!(This->Flags & SFLAG_DONOTFREE)) {
3340 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3341 This->resource.allocatedMemory = NULL;
3342 This->resource.heapMemory = NULL;
3343 } else {
3344 This->Flags &= ~SFLAG_INSYSMEM;
3345 }
3346 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3347 * path is never entered
3348 */
3349 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3350
3351 return WINED3D_OK;
3352 } else if(Src) {
3353 /* Blit from offscreen surface to render target */
3354 float glTexCoord[4];
3355 DWORD oldCKeyFlags = Src->CKeyFlags;
3356 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3357 RECT SourceRectangle;
3358 BOOL paletteOverride = FALSE;
3359
3360 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3361
3362 if(SrcRect) {
3363 SourceRectangle.left = SrcRect->left;
3364 SourceRectangle.right = SrcRect->right;
3365 SourceRectangle.top = SrcRect->top;
3366 SourceRectangle.bottom = SrcRect->bottom;
3367 } else {
3368 SourceRectangle.left = 0;
3369 SourceRectangle.right = Src->currentDesc.Width;
3370 SourceRectangle.top = 0;
3371 SourceRectangle.bottom = Src->currentDesc.Height;
3372 }
3373
3374 /* When blitting from an offscreen surface to a rendertarget, the source
3375 * surface is not required to have a palette. Our rendering / conversion
3376 * code further down the road retrieves the palette from the surface, so
3377 * it must have a palette set. */
3378 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3379 paletteOverride = TRUE;
3380 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3381 Src->palette = This->palette;
3382 }
3383
3384 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3385 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3386 TRACE("Using stretch_rect_fbo\n");
3387 /* The source is always a texture, but never the currently active render target, and the texture
3388 * contents are never upside down
3389 */
3390 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3391 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3392
3393 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3394 if(paletteOverride)
3395 Src->palette = NULL;
3396 return WINED3D_OK;
3397 }
3398
3399 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3400 /* Fall back to software */
3401 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3402 SourceRectangle.left, SourceRectangle.top,
3403 SourceRectangle.right, SourceRectangle.bottom);
3404 return WINED3DERR_INVALIDCALL;
3405 }
3406
3407 /* Color keying: Check if we have to do a color keyed blt,
3408 * and if not check if a color key is activated.
3409 *
3410 * Just modify the color keying parameters in the surface and restore them afterwards
3411 * The surface keeps track of the color key last used to load the opengl surface.
3412 * PreLoad will catch the change to the flags and color key and reload if necessary.
3413 */
3414 if(Flags & WINEDDBLT_KEYSRC) {
3415 /* Use color key from surface */
3416 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3417 /* Use color key from DDBltFx */
3418 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3419 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3420 } else {
3421 /* Do not use color key */
3422 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3423 }
3424
3425 /* Now load the surface */
3426 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3427
3428 /* Activate the destination context, set it up for blitting */
3429 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3430
3431 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3432 * while OpenGL coordinates are window relative.
3433 * Also beware of the origin difference(top left vs bottom left).
3434 * Also beware that the front buffer's surface size is screen width x screen height,
3435 * whereas the real gl drawable size is the size of the window.
3436 */
3437 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3438 RECT windowsize;
3439 POINT offset = {0,0};
3440 UINT h;
3441 ClientToScreen(dstSwapchain->win_handle, &offset);
3442 GetClientRect(dstSwapchain->win_handle, &windowsize);
3443 h = windowsize.bottom - windowsize.top;
3444 rect.x1 -= offset.x; rect.x2 -=offset.x;
3445 rect.y1 -= offset.y; rect.y2 -=offset.y;
3446 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3447 }
3448
3449 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format,
3450 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3451
3452 ENTER_GL();
3453
3454 /* Bind the texture */
3455 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3456 checkGLcall("glBindTexture");
3457
3458 /* Filtering for StretchRect */
3459 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3460 magLookup[Filter - WINED3DTEXF_NONE]);
3461 checkGLcall("glTexParameteri");
3462 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3463 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3464 checkGLcall("glTexParameteri");
3465 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3466 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3467 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3468 checkGLcall("glTexEnvi");
3469
3470 /* This is for color keying */
3471 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3472 glEnable(GL_ALPHA_TEST);
3473 checkGLcall("glEnable GL_ALPHA_TEST");
3474
3475 /* When the primary render target uses P8, the alpha component contains the palette index.
3476 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3477 * should be masked away have alpha set to 0. */
3478 if(primary_render_target_is_p8(myDevice))
3479 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3480 else
3481 glAlphaFunc(GL_NOTEQUAL, 0.0);
3482 checkGLcall("glAlphaFunc\n");
3483 } else {
3484 glDisable(GL_ALPHA_TEST);
3485 checkGLcall("glDisable GL_ALPHA_TEST");
3486 }
3487
3488 /* Draw a textured quad
3489 */
3490 glBegin(GL_QUADS);
3491
3492 glColor3d(1.0f, 1.0f, 1.0f);
3493 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3494 glVertex3f(rect.x1,
3495 rect.y1,
3496 0.0);
3497
3498 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3499 glVertex3f(rect.x1, rect.y2, 0.0);
3500
3501 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3502 glVertex3f(rect.x2,
3503 rect.y2,
3504 0.0);
3505
3506 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3507 glVertex3f(rect.x2,
3508 rect.y1,
3509 0.0);
3510 glEnd();
3511 checkGLcall("glEnd");
3512
3513 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3514 glDisable(GL_ALPHA_TEST);
3515 checkGLcall("glDisable(GL_ALPHA_TEST)");
3516 }
3517
3518 glBindTexture(Src->glDescription.target, 0);
3519 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3520
3521 /* Restore the color key parameters */
3522 Src->CKeyFlags = oldCKeyFlags;
3523 Src->SrcBltCKey = oldBltCKey;
3524
3525 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3526 if(paletteOverride)
3527 Src->palette = NULL;
3528
3529 LEAVE_GL();
3530
3531 /* Leave the opengl state valid for blitting */
3532 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3533
3534 /* Flush in case the drawable is used by multiple GL contexts */
3535 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3536 glFlush();
3537
3538 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3539 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3540 * is outdated now
3541 */
3542 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3543
3544 return WINED3D_OK;
3545 } else {
3546 /* Source-Less Blit to render target */
3547 if (Flags & WINEDDBLT_COLORFILL) {
3548 /* This is easy to handle for the D3D Device... */
3549 DWORD color;
3550
3551 TRACE("Colorfill\n");
3552
3553 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3554 must be true if we are here */
3555 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3556 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3557 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3558 TRACE("Surface is higher back buffer, falling back to software\n");
3559 return WINED3DERR_INVALIDCALL;
3560 }
3561
3562 /* The color as given in the Blt function is in the format of the frame-buffer...
3563 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3564 */
3565 if (This->resource.format == WINED3DFMT_P8) {
3566 DWORD alpha;
3567
3568 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3569 else alpha = 0xFF000000;
3570
3571 if (This->palette) {
3572 color = (alpha |
3573 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3574 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3575 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3576 } else {
3577 color = alpha;
3578 }
3579 }
3580 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3581 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3582 color = 0xFFFFFFFF;
3583 } else {
3584 color = ((0xFF000000) |
3585 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3586 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3587 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3588 }
3589 }
3590 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3591 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3592 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3593 }
3594 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3595 color = DDBltFx->u5.dwFillColor;
3596 }
3597 else {
3598 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3599 return WINED3DERR_INVALIDCALL;
3600 }
3601
3602 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3603 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3604 1, /* Number of rectangles */
3605 &rect, WINED3DCLEAR_TARGET, color,
3606 0.0 /* Z */,
3607 0 /* Stencil */);
3608 return WINED3D_OK;
3609 }
3610 }
3611
3612 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3613 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3614 return WINED3DERR_INVALIDCALL;
3615 }
3616
3617 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3618 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3619 {
3620 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3621 float depth;
3622
3623 if (Flags & WINEDDBLT_DEPTHFILL) {
3624 switch(This->resource.format) {
3625 case WINED3DFMT_D16:
3626 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3627 break;
3628 case WINED3DFMT_D15S1:
3629 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3630 break;
3631 case WINED3DFMT_D24S8:
3632 case WINED3DFMT_D24X8:
3633 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3634 break;
3635 case WINED3DFMT_D32:
3636 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3637 break;
3638 default:
3639 depth = 0.0;
3640 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3641 }
3642
3643 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3644 DestRect == NULL ? 0 : 1,
3645 (const WINED3DRECT *)DestRect,
3646 WINED3DCLEAR_ZBUFFER,
3647 0x00000000,
3648 depth,
3649 0x00000000);
3650 }
3651
3652 FIXME("(%p): Unsupp depthstencil blit\n", This);
3653 return WINED3DERR_INVALIDCALL;
3654 }
3655
3656 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3657 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3658 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3659 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3660 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3661 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3662 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3663
3664 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3665 {
3666 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3667 return WINEDDERR_SURFACEBUSY;
3668 }
3669
3670 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3671 * except depth blits, which seem to work
3672 */
3673 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3674 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3675 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3676 return WINED3DERR_INVALIDCALL;
3677 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3678 TRACE("Z Blit override handled the blit\n");
3679 return WINED3D_OK;
3680 }
3681 }
3682
3683 /* Special cases for RenderTargets */
3684 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3685 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3686 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3687 }
3688
3689 /* For the rest call the X11 surface implementation.
3690 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3691 * other Blts are rather rare
3692 */
3693 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3694 }
3695
3696 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3697 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3698 {
3699 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3700 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3701 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3702 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3703
3704 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3705 {
3706 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3707 return WINEDDERR_SURFACEBUSY;
3708 }
3709
3710 if(myDevice->inScene &&
3711 (iface == myDevice->stencilBufferTarget ||
3712 (Source && Source == myDevice->stencilBufferTarget))) {
3713 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3714 return WINED3DERR_INVALIDCALL;
3715 }
3716
3717 /* Special cases for RenderTargets */
3718 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3719 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3720
3721 RECT SrcRect, DstRect;
3722 DWORD Flags=0;
3723
3724 if(rsrc) {
3725 SrcRect.left = rsrc->left;
3726 SrcRect.top= rsrc->top;
3727 SrcRect.bottom = rsrc->bottom;
3728 SrcRect.right = rsrc->right;
3729 } else {
3730 SrcRect.left = 0;
3731 SrcRect.top = 0;
3732 SrcRect.right = srcImpl->currentDesc.Width;
3733 SrcRect.bottom = srcImpl->currentDesc.Height;
3734 }
3735
3736 DstRect.left = dstx;
3737 DstRect.top=dsty;
3738 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3739 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3740
3741 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3742 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3743 Flags |= WINEDDBLT_KEYSRC;
3744 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3745 Flags |= WINEDDBLT_KEYDEST;
3746 if(trans & WINEDDBLTFAST_WAIT)
3747 Flags |= WINEDDBLT_WAIT;
3748 if(trans & WINEDDBLTFAST_DONOTWAIT)
3749 Flags |= WINEDDBLT_DONOTWAIT;
3750
3751 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3752 }
3753
3754
3755 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3756 }
3757
3758 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3759 {
3760 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3761 RGBQUAD col[256];
3762 IWineD3DPaletteImpl *pal = This->palette;
3763 unsigned int n;
3764 TRACE("(%p)\n", This);
3765
3766 if (!pal) return WINED3D_OK;
3767
3768 if(This->resource.format == WINED3DFMT_P8 ||
3769 This->resource.format == WINED3DFMT_A8P8)
3770 {
3771 int bpp;
3772 GLenum format, internal, type;
3773 CONVERT_TYPES convert;
3774
3775 /* Check if we are using a RTL mode which uses texturing for uploads */
3776 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3777
3778 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3779 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3780
3781 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3782 {
3783 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3784 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3785
3786 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3787 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3788
3789 /* Re-upload the palette */
3790 d3dfmt_p8_upload_palette(iface, convert);
3791 } else {
3792 if(!(This->Flags & SFLAG_INSYSMEM)) {
3793 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3794 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3795 }
3796 TRACE("Dirtifying surface\n");
3797 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3798 }
3799 }
3800
3801 if(This->Flags & SFLAG_DIBSECTION) {
3802 TRACE("(%p): Updating the hdc's palette\n", This);
3803 for (n=0; n<256; n++) {
3804 col[n].rgbRed = pal->palents[n].peRed;
3805 col[n].rgbGreen = pal->palents[n].peGreen;
3806 col[n].rgbBlue = pal->palents[n].peBlue;
3807 col[n].rgbReserved = 0;
3808 }
3809 SetDIBColorTable(This->hDC, 0, 256, col);
3810 }
3811
3812 /* Propagate the changes to the drawable when we have a palette. */
3813 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3814 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3815
3816 return WINED3D_OK;
3817 }
3818
3819 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3820 /** Check against the maximum texture sizes supported by the video card **/
3821 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3822 unsigned int pow2Width, pow2Height;
3823 const struct GlPixelFormatDesc *glDesc;
3824
3825 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3826 /* Setup some glformat defaults */
3827 This->glDescription.glFormat = glDesc->glFormat;
3828 This->glDescription.glFormatInternal = glDesc->glInternal;
3829 This->glDescription.glType = glDesc->glType;
3830
3831 This->glDescription.textureName = 0;
3832 This->glDescription.target = GL_TEXTURE_2D;
3833
3834 /* Non-power2 support */
3835 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3836 pow2Width = This->currentDesc.Width;
3837 pow2Height = This->currentDesc.Height;
3838 } else {
3839 /* Find the nearest pow2 match */
3840 pow2Width = pow2Height = 1;
3841 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3842 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3843 }
3844 This->pow2Width = pow2Width;
3845 This->pow2Height = pow2Height;
3846
3847 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3848 WINED3DFORMAT Format = This->resource.format;
3849 /** TODO: add support for non power two compressed textures **/
3850 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3851 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3852 || This->resource.format == WINED3DFMT_ATI2N) {
3853 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3854 This, This->currentDesc.Width, This->currentDesc.Height);
3855 return WINED3DERR_NOTAVAILABLE;
3856 }
3857 }
3858
3859 if(pow2Width != This->currentDesc.Width ||
3860 pow2Height != This->currentDesc.Height) {
3861 This->Flags |= SFLAG_NONPOW2;
3862 }
3863
3864 TRACE("%p\n", This);
3865 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3866 /* one of three options
3867 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)
3868 2: Set the texture to the maximum size (bad idea)
3869 3: WARN and return WINED3DERR_NOTAVAILABLE;
3870 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.
3871 */
3872 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
3873 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
3874 This->Flags |= SFLAG_OVERSIZE;
3875
3876 /* This will be initialized on the first blt */
3877 This->glRect.left = 0;
3878 This->glRect.top = 0;
3879 This->glRect.right = 0;
3880 This->glRect.bottom = 0;
3881 } else {
3882 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3883 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3884 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3885 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3886 */
3887 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3888 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3889 {
3890 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3891 This->pow2Width = This->currentDesc.Width;
3892 This->pow2Height = This->currentDesc.Height;
3893 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3894 }
3895
3896 /* No oversize, gl rect is the full texture size */
3897 This->Flags &= ~SFLAG_OVERSIZE;
3898 This->glRect.left = 0;
3899 This->glRect.top = 0;
3900 This->glRect.right = This->pow2Width;
3901 This->glRect.bottom = This->pow2Height;
3902 }
3903
3904 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3905 switch(wined3d_settings.offscreen_rendering_mode) {
3906 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3907 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3908 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3909 }
3910 }
3911
3912 This->Flags |= SFLAG_INSYSMEM;
3913
3914 return WINED3D_OK;
3915 }
3916
3917 struct depth_blt_info
3918 {
3919 GLenum binding;
3920 GLenum bind_target;
3921 enum tex_types tex_type;
3922 GLfloat coords[4][3];
3923 };
3924
3925 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3926 {
3927 GLfloat (*coords)[3] = info->coords;
3928
3929 switch (target)
3930 {
3931 default:
3932 FIXME("Unsupported texture target %#x\n", target);
3933 /* Fall back to GL_TEXTURE_2D */
3934 case GL_TEXTURE_2D:
3935 info->binding = GL_TEXTURE_BINDING_2D;
3936 info->bind_target = GL_TEXTURE_2D;
3937 info->tex_type = tex_2d;
3938 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
3939 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
3940 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
3941 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
3942 break;
3943
3944 case GL_TEXTURE_RECTANGLE_ARB:
3945 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
3946 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
3947 info->tex_type = tex_rect;
3948 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
3949 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
3950 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
3951 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
3952 break;
3953
3954 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3955 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
3956 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
3957 info->tex_type = tex_cube;
3958 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
3959 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
3960 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
3961 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
3962
3963 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3964 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
3965 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
3966 info->tex_type = tex_cube;
3967 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
3968 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
3969 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
3970 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
3971
3972 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3973 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
3974 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
3975 info->tex_type = tex_cube;
3976 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
3977 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
3978 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
3979 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
3980
3981 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3982 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
3983 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
3984 info->tex_type = tex_cube;
3985 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
3986 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
3987 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
3988 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
3989
3990 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3991 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
3992 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
3993 info->tex_type = tex_cube;
3994 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
3995 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
3996 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
3997 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
3998
3999 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4000 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4001 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4002 info->tex_type = tex_cube;
4003 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4004 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4005 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4006 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4007 }
4008 }
4009
4010 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4011 {
4012 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4013 struct depth_blt_info info;
4014 GLint old_binding = 0;
4015
4016 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4017
4018 glDisable(GL_CULL_FACE);
4019 glEnable(GL_BLEND);
4020 glDisable(GL_ALPHA_TEST);
4021 glDisable(GL_SCISSOR_TEST);
4022 glDisable(GL_STENCIL_TEST);
4023 glEnable(GL_DEPTH_TEST);
4024 glDepthFunc(GL_ALWAYS);
4025 glDepthMask(GL_TRUE);
4026 glBlendFunc(GL_ZERO, GL_ONE);
4027 glViewport(0, 0, w, h);
4028
4029 surface_get_depth_blt_info(target, w, h, &info);
4030 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4031 glGetIntegerv(info.binding, &old_binding);
4032 glBindTexture(info.bind_target, texture);
4033
4034 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4035
4036 glBegin(GL_TRIANGLE_STRIP);
4037 glTexCoord3fv(info.coords[0]);
4038 glVertex2f(-1.0f, -1.0f);
4039 glTexCoord3fv(info.coords[1]);
4040 glVertex2f(1.0f, -1.0f);
4041 glTexCoord3fv(info.coords[2]);
4042 glVertex2f(-1.0f, 1.0f);
4043 glTexCoord3fv(info.coords[3]);
4044 glVertex2f(1.0f, 1.0f);
4045 glEnd();
4046
4047 glBindTexture(info.bind_target, old_binding);
4048
4049 glPopAttrib();
4050
4051 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4052 }
4053
4054 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4055 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4056
4057 TRACE("(%p) New location %#x\n", This, location);
4058
4059 if (location & ~SFLAG_DS_LOCATIONS) {
4060 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4061 }
4062
4063 This->Flags &= ~SFLAG_DS_LOCATIONS;
4064 This->Flags |= location;
4065 }
4066
4067 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4068 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4069 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4070
4071 TRACE("(%p) New location %#x\n", This, location);
4072
4073 /* TODO: Make this work for modes other than FBO */
4074 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4075
4076 if (This->Flags & location) {
4077 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4078 return;
4079 }
4080
4081 if (This->current_renderbuffer) {
4082 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4083 return;
4084 }
4085
4086 if (location == SFLAG_DS_OFFSCREEN) {
4087 if (This->Flags & SFLAG_DS_ONSCREEN) {
4088 GLint old_binding = 0;
4089 GLenum bind_target;
4090
4091 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4092
4093 ENTER_GL();
4094
4095 if (!device->depth_blt_texture) {
4096 glGenTextures(1, &device->depth_blt_texture);
4097 }
4098
4099 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4100 * directly on the FBO texture. That's because we need to flip. */
4101 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4102 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4103 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4104 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4105 } else {
4106 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4107 bind_target = GL_TEXTURE_2D;
4108 }
4109 glBindTexture(bind_target, device->depth_blt_texture);
4110 glCopyTexImage2D(bind_target,
4111 This->glDescription.level,
4112 This->glDescription.glFormatInternal,
4113 0,
4114 0,
4115 This->currentDesc.Width,
4116 This->currentDesc.Height,
4117 0);
4118 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4119 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4120 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4121 glBindTexture(bind_target, old_binding);
4122
4123 /* Setup the destination */
4124 if (!device->depth_blt_rb) {
4125 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4126 checkGLcall("glGenRenderbuffersEXT");
4127 }
4128 if (device->depth_blt_rb_w != This->currentDesc.Width
4129 || device->depth_blt_rb_h != This->currentDesc.Height) {
4130 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4131 checkGLcall("glBindRenderbufferEXT");
4132 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4133 checkGLcall("glRenderbufferStorageEXT");
4134 device->depth_blt_rb_w = This->currentDesc.Width;
4135 device->depth_blt_rb_h = This->currentDesc.Height;
4136 }
4137
4138 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4139 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4140 checkGLcall("glFramebufferRenderbufferEXT");
4141 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4142
4143 /* Do the actual blit */
4144 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4145 checkGLcall("depth_blt");
4146
4147 if (device->activeContext->current_fbo) {
4148 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4149 } else {
4150 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4151 checkGLcall("glBindFramebuffer()");
4152 }
4153
4154 LEAVE_GL();
4155 } else {
4156 FIXME("No up to date depth stencil location\n");
4157 }
4158 } else if (location == SFLAG_DS_ONSCREEN) {
4159 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4160 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4161
4162 ENTER_GL();
4163
4164 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4165 checkGLcall("glBindFramebuffer()");
4166 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4167 checkGLcall("depth_blt");
4168
4169 if (device->activeContext->current_fbo) {
4170 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4171 checkGLcall("glBindFramebuffer()");
4172 }
4173
4174 LEAVE_GL();
4175 } else {
4176 FIXME("No up to date depth stencil location\n");
4177 }
4178 } else {
4179 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4180 }
4181
4182 This->Flags |= location;
4183 }
4184
4185 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4186 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4187 IWineD3DBaseTexture *texture;
4188 IWineD3DSurfaceImpl *overlay;
4189
4190 TRACE("(%p)->(%s, %s)\n", iface,
4191 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4192 persistent ? "TRUE" : "FALSE");
4193
4194 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4195 IWineD3DSwapChain *swapchain = NULL;
4196
4197 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4198 TRACE("Surface %p is an onscreen surface\n", iface);
4199
4200 IWineD3DSwapChain_Release(swapchain);
4201 } else {
4202 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4203 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4204 }
4205 }
4206
4207 if(persistent) {
4208 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
4209 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4210 TRACE("Passing to container\n");
4211 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4212 IWineD3DBaseTexture_Release(texture);
4213 }
4214 }
4215 This->Flags &= ~SFLAG_LOCATIONS;
4216 This->Flags |= flag;
4217
4218 /* Redraw emulated overlays, if any */
4219 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4220 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4221 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4222 }
4223 }
4224 } else {
4225 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
4226 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4227 TRACE("Passing to container\n");
4228 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4229 IWineD3DBaseTexture_Release(texture);
4230 }
4231 }
4232 This->Flags &= ~flag;
4233 }
4234 }
4235
4236 struct coords {
4237 GLfloat x, y, z;
4238 };
4239
4240 struct float_rect
4241 {
4242 float l;
4243 float t;
4244 float r;
4245 float b;
4246 };
4247
4248 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4249 {
4250 f->l = ((r->left * 2.0f) / w) - 1.0f;
4251 f->t = ((r->top * 2.0f) / h) - 1.0f;
4252 f->r = ((r->right * 2.0f) / w) - 1.0f;
4253 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4254 }
4255
4256 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4257 struct coords coords[4];
4258 RECT rect;
4259 IWineD3DSwapChain *swapchain;
4260 IWineD3DBaseTexture *texture;
4261 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4262 GLenum bind_target;
4263 struct float_rect f;
4264
4265 if(rect_in) {
4266 rect = *rect_in;
4267 } else {
4268 rect.left = 0;
4269 rect.top = 0;
4270 rect.right = This->currentDesc.Width;
4271 rect.bottom = This->currentDesc.Height;
4272 }
4273
4274 switch(This->glDescription.target)
4275 {
4276 case GL_TEXTURE_2D:
4277 bind_target = GL_TEXTURE_2D;
4278
4279 coords[0].x = (float)rect.left / This->pow2Width;
4280 coords[0].y = (float)rect.top / This->pow2Height;
4281 coords[0].z = 0;
4282
4283 coords[1].x = (float)rect.left / This->pow2Width;
4284 coords[1].y = (float)rect.bottom / This->pow2Height;
4285 coords[1].z = 0;
4286
4287 coords[2].x = (float)rect.right / This->pow2Width;
4288 coords[2].y = (float)rect.bottom / This->pow2Height;
4289 coords[2].z = 0;
4290
4291 coords[3].x = (float)rect.right / This->pow2Width;
4292 coords[3].y = (float)rect.top / This->pow2Height;
4293 coords[3].z = 0;
4294 break;
4295
4296 case GL_TEXTURE_RECTANGLE_ARB:
4297 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4298 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4299 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4300 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4301 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4302 break;
4303
4304 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4305 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4306 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4307 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4308 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4309 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4310 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4311 break;
4312
4313 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4314 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4315 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4316 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4317 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4318 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4319 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4320 break;
4321
4322 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4323 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4324 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4325 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4326 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4327 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4328 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4329 break;
4330
4331 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4332 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4333 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4334 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4335 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4336 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4337 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4338 break;
4339
4340 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4341 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4342 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4343 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4344 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4345 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4346 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4347 break;
4348
4349 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4350 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4351 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4352 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4353 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4354 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4355 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4356 break;
4357
4358 default:
4359 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4360 return;
4361 }
4362
4363 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4364 ENTER_GL();
4365
4366 glEnable(bind_target);
4367 checkGLcall("glEnable(bind_target)");
4368 glBindTexture(bind_target, This->glDescription.textureName);
4369 checkGLcall("bind_target, This->glDescription.textureName)");
4370 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4371 checkGLcall("glTexParameteri");
4372 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4373 checkGLcall("glTexParameteri");
4374
4375 if (device->render_offscreen)
4376 {
4377 LONG tmp = rect.top;
4378 rect.top = rect.bottom;
4379 rect.bottom = tmp;
4380 }
4381
4382 glBegin(GL_QUADS);
4383 glTexCoord3fv(&coords[0].x);
4384 glVertex2i(rect.left, rect.top);
4385
4386 glTexCoord3fv(&coords[1].x);
4387 glVertex2i(rect.left, rect.bottom);
4388
4389 glTexCoord3fv(&coords[2].x);
4390 glVertex2i(rect.right, rect.bottom);
4391
4392 glTexCoord3fv(&coords[3].x);
4393 glVertex2i(rect.right, rect.top);
4394 glEnd();
4395 checkGLcall("glEnd");
4396
4397 glDisable(bind_target);
4398 checkGLcall("glDisable(bind_target)");
4399
4400 LEAVE_GL();
4401
4402 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4403 {
4404 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4405 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4406 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4407 glFlush();
4408
4409 IWineD3DSwapChain_Release(swapchain);
4410 } else {
4411 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4412 * reset properly next draw
4413 */
4414 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4415 {
4416 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4417 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4418 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4419 IWineD3DBaseTexture_Release(texture);
4420 }
4421 }
4422 }
4423
4424 /*****************************************************************************
4425 * IWineD3DSurface::LoadLocation
4426 *
4427 * Copies the current surface data from wherever it is to the requested
4428 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4429 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4430 * multiple locations, the gl texture is preferred over the drawable, which is
4431 * preferred over system memory. The PBO counts as system memory. If rect is
4432 * not NULL, only the specified rectangle is copied (only supported for
4433 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4434 * location is marked up to date after the copy.
4435 *
4436 * Parameters:
4437 * flag: Surface location flag to be updated
4438 * rect: rectangle to be copied
4439 *
4440 * Returns:
4441 * WINED3D_OK on success
4442 * WINED3DERR_DEVICELOST on an internal error
4443 *
4444 *****************************************************************************/
4445 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4446 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4447 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4448 IWineD3DSwapChain *swapchain = NULL;
4449 GLenum format, internal, type;
4450 CONVERT_TYPES convert;
4451 int bpp;
4452 int width, pitch, outpitch;
4453 BYTE *mem;
4454
4455 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4456 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4457 TRACE("Surface %p is an onscreen surface\n", iface);
4458
4459 IWineD3DSwapChain_Release(swapchain);
4460 } else {
4461 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4462 * Prefer SFLAG_INTEXTURE. */
4463 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4464 }
4465 }
4466
4467 TRACE("(%p)->(%s, %p)\n", iface,
4468 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4469 rect);
4470 if(rect) {
4471 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4472 }
4473
4474 if(This->Flags & flag) {
4475 TRACE("Location already up to date\n");
4476 return WINED3D_OK;
4477 }
4478
4479 if(!(This->Flags & SFLAG_LOCATIONS)) {
4480 ERR("Surface does not have any up to date location\n");
4481 This->Flags |= SFLAG_LOST;
4482 return WINED3DERR_DEVICELOST;
4483 }
4484
4485 if(flag == SFLAG_INSYSMEM) {
4486 surface_prepare_system_memory(This);
4487
4488 /* Download the surface to system memory */
4489 if(This->Flags & SFLAG_INTEXTURE) {
4490 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4491 surface_bind_and_dirtify(This);
4492
4493 surface_download_data(This);
4494 } else {
4495 read_from_framebuffer(This, rect,
4496 This->resource.allocatedMemory,
4497 IWineD3DSurface_GetPitch(iface));
4498 }
4499 } else if(flag == SFLAG_INDRAWABLE) {
4500 if(This->Flags & SFLAG_INTEXTURE) {
4501 surface_blt_to_drawable(This, rect);
4502 } else {
4503 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4504
4505 /* The width is in 'length' not in bytes */
4506 width = This->currentDesc.Width;
4507 pitch = IWineD3DSurface_GetPitch(iface);
4508
4509 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4510 * but it isn't set (yet) in all cases it is getting called. */
4511 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4512 TRACE("Removing the pbo attached to surface %p\n", This);
4513 surface_remove_pbo(This);
4514 }
4515
4516 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4517 int height = This->currentDesc.Height;
4518
4519 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4520 outpitch = width * bpp;
4521 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4522
4523 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4524 if(!mem) {
4525 ERR("Out of memory %d, %d!\n", outpitch, height);
4526 return WINED3DERR_OUTOFVIDEOMEMORY;
4527 }
4528 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4529
4530 This->Flags |= SFLAG_CONVERTED;
4531 } else {
4532 This->Flags &= ~SFLAG_CONVERTED;
4533 mem = This->resource.allocatedMemory;
4534 }
4535
4536 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4537
4538 /* Don't delete PBO memory */
4539 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4540 HeapFree(GetProcessHeap(), 0, mem);
4541 }
4542 } else /* if(flag == SFLAG_INTEXTURE) */ {
4543 if (This->Flags & SFLAG_INDRAWABLE) {
4544 read_from_framebuffer_texture(This);
4545 } else { /* Upload from system memory */
4546 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4547
4548 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4549 surface_bind_and_dirtify(This);
4550
4551 /* The only place where LoadTexture() might get called when isInDraw=1
4552 * is ActivateContext where lastActiveRenderTarget is preloaded.
4553 */
4554 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4555 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4556
4557 /* Otherwise: System memory copy must be most up to date */
4558
4559 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4560 This->Flags |= SFLAG_GLCKEY;
4561 This->glCKey = This->SrcBltCKey;
4562 }
4563 else This->Flags &= ~SFLAG_GLCKEY;
4564
4565 /* The width is in 'length' not in bytes */
4566 width = This->currentDesc.Width;
4567 pitch = IWineD3DSurface_GetPitch(iface);
4568
4569 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4570 * but it isn't set (yet) in all cases it is getting called. */
4571 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4572 TRACE("Removing the pbo attached to surface %p\n", This);
4573 surface_remove_pbo(This);
4574 }
4575
4576 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4577 int height = This->currentDesc.Height;
4578
4579 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4580 outpitch = width * bpp;
4581 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4582
4583 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4584 if(!mem) {
4585 ERR("Out of memory %d, %d!\n", outpitch, height);
4586 return WINED3DERR_OUTOFVIDEOMEMORY;
4587 }
4588 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4589
4590 This->Flags |= SFLAG_CONVERTED;
4591 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4592 d3dfmt_p8_upload_palette(iface, convert);
4593 This->Flags &= ~SFLAG_CONVERTED;
4594 mem = This->resource.allocatedMemory;
4595 } else {
4596 This->Flags &= ~SFLAG_CONVERTED;
4597 mem = This->resource.allocatedMemory;
4598 }
4599
4600 /* Make sure the correct pitch is used */
4601 ENTER_GL();
4602 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4603 LEAVE_GL();
4604
4605 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4606 TRACE("non power of two support\n");
4607 if(!(This->Flags & SFLAG_ALLOCATED)) {
4608 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4609 }
4610 if (mem || (This->Flags & SFLAG_PBO)) {
4611 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4612 }
4613 } else {
4614 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4615 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4616 */
4617 if(!(This->Flags & SFLAG_ALLOCATED)) {
4618 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4619 }
4620 if (mem || (This->Flags & SFLAG_PBO)) {
4621 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4622 }
4623 }
4624
4625 /* Restore the default pitch */
4626 ENTER_GL();
4627 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4628 LEAVE_GL();
4629
4630 /* Don't delete PBO memory */
4631 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4632 HeapFree(GetProcessHeap(), 0, mem);
4633 }
4634 }
4635
4636 if(rect == NULL) {
4637 This->Flags |= flag;
4638 }
4639
4640 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4641 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4642 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4643 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4644 }
4645
4646 return WINED3D_OK;
4647 }
4648
4649 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4650 {
4651 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4652 IWineD3DSwapChain *swapchain = NULL;
4653
4654 /* Update the drawable size method */
4655 if(container) {
4656 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4657 }
4658 if(swapchain) {
4659 This->get_drawable_size = get_drawable_size_swapchain;
4660 IWineD3DSwapChain_Release(swapchain);
4661 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4662 switch(wined3d_settings.offscreen_rendering_mode) {
4663 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4664 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4665 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4666 }
4667 }
4668
4669 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4670 }
4671
4672 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4673 return SURFACE_OPENGL;
4674 }
4675
4676 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4677 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4678 HRESULT hr;
4679
4680 /* If there's no destination surface there is nothing to do */
4681 if(!This->overlay_dest) return WINED3D_OK;
4682
4683 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4684 * update the overlay. Prevent an endless recursion
4685 */
4686 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4687 return WINED3D_OK;
4688 }
4689 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4690 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4691 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4692 NULL, WINED3DTEXF_LINEAR);
4693 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4694
4695 return hr;
4696 }
4697
4698 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4699 {
4700 /* IUnknown */
4701 IWineD3DBaseSurfaceImpl_QueryInterface,
4702 IWineD3DBaseSurfaceImpl_AddRef,
4703 IWineD3DSurfaceImpl_Release,
4704 /* IWineD3DResource */
4705 IWineD3DBaseSurfaceImpl_GetParent,
4706 IWineD3DBaseSurfaceImpl_GetDevice,
4707 IWineD3DBaseSurfaceImpl_SetPrivateData,
4708 IWineD3DBaseSurfaceImpl_GetPrivateData,
4709 IWineD3DBaseSurfaceImpl_FreePrivateData,
4710 IWineD3DBaseSurfaceImpl_SetPriority,
4711 IWineD3DBaseSurfaceImpl_GetPriority,
4712 IWineD3DSurfaceImpl_PreLoad,
4713 IWineD3DSurfaceImpl_UnLoad,
4714 IWineD3DBaseSurfaceImpl_GetType,
4715 /* IWineD3DSurface */
4716 IWineD3DBaseSurfaceImpl_GetContainer,
4717 IWineD3DBaseSurfaceImpl_GetDesc,
4718 IWineD3DSurfaceImpl_LockRect,
4719 IWineD3DSurfaceImpl_UnlockRect,
4720 IWineD3DSurfaceImpl_GetDC,
4721 IWineD3DSurfaceImpl_ReleaseDC,
4722 IWineD3DSurfaceImpl_Flip,
4723 IWineD3DSurfaceImpl_Blt,
4724 IWineD3DBaseSurfaceImpl_GetBltStatus,
4725 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4726 IWineD3DBaseSurfaceImpl_IsLost,
4727 IWineD3DBaseSurfaceImpl_Restore,
4728 IWineD3DSurfaceImpl_BltFast,
4729 IWineD3DBaseSurfaceImpl_GetPalette,
4730 IWineD3DBaseSurfaceImpl_SetPalette,
4731 IWineD3DSurfaceImpl_RealizePalette,
4732 IWineD3DBaseSurfaceImpl_SetColorKey,
4733 IWineD3DBaseSurfaceImpl_GetPitch,
4734 IWineD3DSurfaceImpl_SetMem,
4735 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4736 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4737 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4738 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4739 IWineD3DBaseSurfaceImpl_SetClipper,
4740 IWineD3DBaseSurfaceImpl_GetClipper,
4741 /* Internal use: */
4742 IWineD3DSurfaceImpl_LoadTexture,
4743 IWineD3DSurfaceImpl_BindTexture,
4744 IWineD3DSurfaceImpl_SaveSnapshot,
4745 IWineD3DSurfaceImpl_SetContainer,
4746 IWineD3DSurfaceImpl_GetGlDesc,
4747 IWineD3DBaseSurfaceImpl_GetData,
4748 IWineD3DSurfaceImpl_SetFormat,
4749 IWineD3DSurfaceImpl_PrivateSetup,
4750 IWineD3DSurfaceImpl_ModifyLocation,
4751 IWineD3DSurfaceImpl_LoadLocation,
4752 IWineD3DSurfaceImpl_GetImplType,
4753 IWineD3DSurfaceImpl_DrawOverlay
4754 };
4755 #undef GLINFO_LOCATION
4756
4757 #define GLINFO_LOCATION device->adapter->gl_info
4758 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4759 static void ffp_blit_free(IWineD3DDevice *iface) { }
4760
4761 static HRESULT ffp_blit_set(IWineD3DDevice *iface, WINED3DFORMAT fmt, GLenum textype, UINT width, UINT height) {
4762 glEnable(textype);
4763 checkGLcall("glEnable(textype)");
4764 return WINED3D_OK;
4765 }
4766
4767 static void ffp_blit_unset(IWineD3DDevice *iface) {
4768 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4769 glDisable(GL_TEXTURE_2D);
4770 checkGLcall("glDisable(GL_TEXTURE_2D)");
4771 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4772 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4773 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4774 }
4775 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4776 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4777 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4778 }
4779 }
4780
4781 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4782 {
4783 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4784 {
4785 TRACE("Checking support for fixup:\n");
4786 dump_color_fixup_desc(fixup);
4787 }
4788
4789 /* We only support identity conversions. */
4790 if (is_identity_fixup(fixup))
4791 {
4792 TRACE("[OK]\n");
4793 return TRUE;
4794 }
4795
4796 TRACE("[FAILED]\n");
4797 return FALSE;
4798 }
4799
4800 const struct blit_shader ffp_blit = {
4801 ffp_blit_alloc,
4802 ffp_blit_free,
4803 ffp_blit_set,
4804 ffp_blit_unset,
4805 ffp_blit_color_fixup_supported
4806 };