2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface
);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d
);
37 #define GLINFO_LOCATION (*gl_info)
39 static void surface_cleanup(IWineD3DSurfaceImpl
*This
)
41 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
42 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
43 struct wined3d_context
*context
= NULL
;
44 renderbuffer_entry_t
*entry
, *entry2
;
46 TRACE("(%p) : Cleaning up.\n", This
);
48 /* Need a context to destroy the texture. Use the currently active render
49 * target, but only if the primary render target exists. Otherwise
50 * lastActiveRenderTarget is garbage. When destroying the primary render
51 * target, Uninit3D() will activate a context before doing anything. */
52 if (device
->render_targets
&& device
->render_targets
[0])
54 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
59 if (This
->texture_name
)
61 /* Release the OpenGL texture. */
62 TRACE("Deleting texture %u.\n", This
->texture_name
);
63 glDeleteTextures(1, &This
->texture_name
);
66 if (This
->Flags
& SFLAG_PBO
)
69 GL_EXTCALL(glDeleteBuffersARB(1, &This
->pbo
));
72 LIST_FOR_EACH_ENTRY_SAFE(entry
, entry2
, &This
->renderbuffers
, renderbuffer_entry_t
, entry
)
74 gl_info
->fbo_ops
.glDeleteRenderbuffers(1, &entry
->id
);
75 HeapFree(GetProcessHeap(), 0, entry
);
80 if (This
->Flags
& SFLAG_DIBSECTION
)
83 SelectObject(This
->hDC
, This
->dib
.holdbitmap
);
85 /* Release the DIB section. */
86 DeleteObject(This
->dib
.DIBsection
);
87 This
->dib
.bitmap_data
= NULL
;
88 This
->resource
.allocatedMemory
= NULL
;
91 if (This
->Flags
& SFLAG_USERPTR
) IWineD3DSurface_SetMem((IWineD3DSurface
*)This
, NULL
);
92 if (This
->overlay_dest
) list_remove(&This
->overlay_entry
);
94 HeapFree(GetProcessHeap(), 0, This
->palette9
);
96 resource_cleanup((IWineD3DResource
*)This
);
98 if (context
) context_release(context
);
101 UINT
surface_calculate_size(const struct wined3d_format_desc
*format_desc
, UINT alignment
, UINT width
, UINT height
)
105 if (format_desc
->format
== WINED3DFMT_UNKNOWN
)
109 else if (format_desc
->Flags
& WINED3DFMT_FLAG_COMPRESSED
)
111 UINT row_block_count
= (width
+ format_desc
->block_width
- 1) / format_desc
->block_width
;
112 UINT row_count
= (height
+ format_desc
->block_height
- 1) / format_desc
->block_height
;
113 size
= row_count
* row_block_count
* format_desc
->block_byte_count
;
117 /* The pitch is a multiple of 4 bytes. */
118 size
= height
* (((width
* format_desc
->byte_count
) + alignment
- 1) & ~(alignment
- 1));
121 if (format_desc
->heightscale
!= 0.0f
) size
*= format_desc
->heightscale
;
130 enum tex_types tex_type
;
131 GLfloat coords
[4][3];
142 static inline void cube_coords_float(const RECT
*r
, UINT w
, UINT h
, struct float_rect
*f
)
144 f
->l
= ((r
->left
* 2.0f
) / w
) - 1.0f
;
145 f
->t
= ((r
->top
* 2.0f
) / h
) - 1.0f
;
146 f
->r
= ((r
->right
* 2.0f
) / w
) - 1.0f
;
147 f
->b
= ((r
->bottom
* 2.0f
) / h
) - 1.0f
;
150 static void surface_get_blt_info(GLenum target
, const RECT
*rect_in
, GLsizei w
, GLsizei h
, struct blt_info
*info
)
152 GLfloat (*coords
)[3] = info
->coords
;
169 FIXME("Unsupported texture target %#x\n", target
);
170 /* Fall back to GL_TEXTURE_2D */
172 info
->binding
= GL_TEXTURE_BINDING_2D
;
173 info
->bind_target
= GL_TEXTURE_2D
;
174 info
->tex_type
= tex_2d
;
175 coords
[0][0] = (float)rect
.left
/ w
;
176 coords
[0][1] = (float)rect
.top
/ h
;
179 coords
[1][0] = (float)rect
.right
/ w
;
180 coords
[1][1] = (float)rect
.top
/ h
;
183 coords
[2][0] = (float)rect
.left
/ w
;
184 coords
[2][1] = (float)rect
.bottom
/ h
;
187 coords
[3][0] = (float)rect
.right
/ w
;
188 coords
[3][1] = (float)rect
.bottom
/ h
;
192 case GL_TEXTURE_RECTANGLE_ARB
:
193 info
->binding
= GL_TEXTURE_BINDING_RECTANGLE_ARB
;
194 info
->bind_target
= GL_TEXTURE_RECTANGLE_ARB
;
195 info
->tex_type
= tex_rect
;
196 coords
[0][0] = rect
.left
; coords
[0][1] = rect
.top
; coords
[0][2] = 0.0f
;
197 coords
[1][0] = rect
.right
; coords
[1][1] = rect
.top
; coords
[1][2] = 0.0f
;
198 coords
[2][0] = rect
.left
; coords
[2][1] = rect
.bottom
; coords
[2][2] = 0.0f
;
199 coords
[3][0] = rect
.right
; coords
[3][1] = rect
.bottom
; coords
[3][2] = 0.0f
;
202 case GL_TEXTURE_CUBE_MAP_POSITIVE_X
:
203 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
204 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
205 info
->tex_type
= tex_cube
;
206 cube_coords_float(&rect
, w
, h
, &f
);
208 coords
[0][0] = 1.0f
; coords
[0][1] = -f
.t
; coords
[0][2] = -f
.l
;
209 coords
[1][0] = 1.0f
; coords
[1][1] = -f
.t
; coords
[1][2] = -f
.r
;
210 coords
[2][0] = 1.0f
; coords
[2][1] = -f
.b
; coords
[2][2] = -f
.l
;
211 coords
[3][0] = 1.0f
; coords
[3][1] = -f
.b
; coords
[3][2] = -f
.r
;
214 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X
:
215 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
216 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
217 info
->tex_type
= tex_cube
;
218 cube_coords_float(&rect
, w
, h
, &f
);
220 coords
[0][0] = -1.0f
; coords
[0][1] = -f
.t
; coords
[0][2] = f
.l
;
221 coords
[1][0] = -1.0f
; coords
[1][1] = -f
.t
; coords
[1][2] = f
.r
;
222 coords
[2][0] = -1.0f
; coords
[2][1] = -f
.b
; coords
[2][2] = f
.l
;
223 coords
[3][0] = -1.0f
; coords
[3][1] = -f
.b
; coords
[3][2] = f
.r
;
226 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y
:
227 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
228 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
229 info
->tex_type
= tex_cube
;
230 cube_coords_float(&rect
, w
, h
, &f
);
232 coords
[0][0] = f
.l
; coords
[0][1] = 1.0f
; coords
[0][2] = f
.t
;
233 coords
[1][0] = f
.r
; coords
[1][1] = 1.0f
; coords
[1][2] = f
.t
;
234 coords
[2][0] = f
.l
; coords
[2][1] = 1.0f
; coords
[2][2] = f
.b
;
235 coords
[3][0] = f
.r
; coords
[3][1] = 1.0f
; coords
[3][2] = f
.b
;
238 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
:
239 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
240 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
241 info
->tex_type
= tex_cube
;
242 cube_coords_float(&rect
, w
, h
, &f
);
244 coords
[0][0] = f
.l
; coords
[0][1] = -1.0f
; coords
[0][2] = -f
.t
;
245 coords
[1][0] = f
.r
; coords
[1][1] = -1.0f
; coords
[1][2] = -f
.t
;
246 coords
[2][0] = f
.l
; coords
[2][1] = -1.0f
; coords
[2][2] = -f
.b
;
247 coords
[3][0] = f
.r
; coords
[3][1] = -1.0f
; coords
[3][2] = -f
.b
;
250 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z
:
251 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
252 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
253 info
->tex_type
= tex_cube
;
254 cube_coords_float(&rect
, w
, h
, &f
);
256 coords
[0][0] = f
.l
; coords
[0][1] = -f
.t
; coords
[0][2] = 1.0f
;
257 coords
[1][0] = f
.r
; coords
[1][1] = -f
.t
; coords
[1][2] = 1.0f
;
258 coords
[2][0] = f
.l
; coords
[2][1] = -f
.b
; coords
[2][2] = 1.0f
;
259 coords
[3][0] = f
.r
; coords
[3][1] = -f
.b
; coords
[3][2] = 1.0f
;
262 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
:
263 info
->binding
= GL_TEXTURE_BINDING_CUBE_MAP_ARB
;
264 info
->bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
265 info
->tex_type
= tex_cube
;
266 cube_coords_float(&rect
, w
, h
, &f
);
268 coords
[0][0] = -f
.l
; coords
[0][1] = -f
.t
; coords
[0][2] = -1.0f
;
269 coords
[1][0] = -f
.r
; coords
[1][1] = -f
.t
; coords
[1][2] = -1.0f
;
270 coords
[2][0] = -f
.l
; coords
[2][1] = -f
.b
; coords
[2][2] = -1.0f
;
271 coords
[3][0] = -f
.r
; coords
[3][1] = -f
.b
; coords
[3][2] = -1.0f
;
276 static inline void surface_get_rect(IWineD3DSurfaceImpl
*This
, const RECT
*rect_in
, RECT
*rect_out
)
279 *rect_out
= *rect_in
;
284 rect_out
->right
= This
->currentDesc
.Width
;
285 rect_out
->bottom
= This
->currentDesc
.Height
;
289 /* GL locking and context activation is done by the caller */
290 void draw_textured_quad(IWineD3DSurfaceImpl
*src_surface
, const RECT
*src_rect
, const RECT
*dst_rect
, WINED3DTEXTUREFILTERTYPE Filter
)
292 IWineD3DBaseTextureImpl
*texture
;
293 struct blt_info info
;
295 surface_get_blt_info(src_surface
->texture_target
, src_rect
, src_surface
->pow2Width
, src_surface
->pow2Height
, &info
);
297 glEnable(info
.bind_target
);
298 checkGLcall("glEnable(bind_target)");
300 /* Bind the texture */
301 glBindTexture(info
.bind_target
, src_surface
->texture_name
);
302 checkGLcall("glBindTexture");
304 /* Filtering for StretchRect */
305 glTexParameteri(info
.bind_target
, GL_TEXTURE_MAG_FILTER
,
306 wined3d_gl_mag_filter(magLookup
, Filter
));
307 checkGLcall("glTexParameteri");
308 glTexParameteri(info
.bind_target
, GL_TEXTURE_MIN_FILTER
,
309 wined3d_gl_min_mip_filter(minMipLookup
, Filter
, WINED3DTEXF_NONE
));
310 checkGLcall("glTexParameteri");
311 glTexParameteri(info
.bind_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
312 glTexParameteri(info
.bind_target
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
313 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
314 checkGLcall("glTexEnvi");
317 glBegin(GL_TRIANGLE_STRIP
);
318 glTexCoord3fv(info
.coords
[0]);
319 glVertex2i(dst_rect
->left
, dst_rect
->top
);
321 glTexCoord3fv(info
.coords
[1]);
322 glVertex2i(dst_rect
->right
, dst_rect
->top
);
324 glTexCoord3fv(info
.coords
[2]);
325 glVertex2i(dst_rect
->left
, dst_rect
->bottom
);
327 glTexCoord3fv(info
.coords
[3]);
328 glVertex2i(dst_rect
->right
, dst_rect
->bottom
);
331 /* Unbind the texture */
332 glBindTexture(info
.bind_target
, 0);
333 checkGLcall("glBindTexture(info->bind_target, 0)");
335 /* We changed the filtering settings on the texture. Inform the
336 * container about this to get the filters reset properly next draw. */
337 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface
*)src_surface
, &IID_IWineD3DBaseTexture
, (void **)&texture
)))
339 texture
->baseTexture
.texture_rgb
.states
[WINED3DTEXSTA_MAGFILTER
] = WINED3DTEXF_POINT
;
340 texture
->baseTexture
.texture_rgb
.states
[WINED3DTEXSTA_MINFILTER
] = WINED3DTEXF_POINT
;
341 texture
->baseTexture
.texture_rgb
.states
[WINED3DTEXSTA_MIPFILTER
] = WINED3DTEXF_NONE
;
342 IWineD3DBaseTexture_Release((IWineD3DBaseTexture
*)texture
);
346 HRESULT
surface_init(IWineD3DSurfaceImpl
*surface
, WINED3DSURFTYPE surface_type
, UINT alignment
,
347 UINT width
, UINT height
, UINT level
, BOOL lockable
, BOOL discard
, WINED3DMULTISAMPLE_TYPE multisample_type
,
348 UINT multisample_quality
, IWineD3DDeviceImpl
*device
, DWORD usage
, WINED3DFORMAT format
,
349 WINED3DPOOL pool
, IUnknown
*parent
, const struct wined3d_parent_ops
*parent_ops
)
351 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
352 const struct wined3d_format_desc
*format_desc
= getFormatDescEntry(format
, gl_info
);
353 void (*cleanup
)(IWineD3DSurfaceImpl
*This
);
354 unsigned int resource_size
;
357 if (multisample_quality
> 0)
359 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality
);
360 multisample_quality
= 0;
363 /* FIXME: Check that the format is supported by the device. */
365 resource_size
= surface_calculate_size(format_desc
, alignment
, width
, height
);
367 /* Look at the implementation and set the correct Vtable. */
368 switch (surface_type
)
371 surface
->lpVtbl
= &IWineD3DSurface_Vtbl
;
372 cleanup
= surface_cleanup
;
376 surface
->lpVtbl
= &IWineGDISurface_Vtbl
;
377 cleanup
= surface_gdi_cleanup
;
381 ERR("Requested unknown surface implementation %#x.\n", surface_type
);
382 return WINED3DERR_INVALIDCALL
;
385 hr
= resource_init((IWineD3DResource
*)surface
, WINED3DRTYPE_SURFACE
,
386 device
, resource_size
, usage
, format_desc
, pool
, parent
, parent_ops
);
389 WARN("Failed to initialize resource, returning %#x.\n", hr
);
393 /* "Standalone" surface. */
394 IWineD3DSurface_SetContainer((IWineD3DSurface
*)surface
, NULL
);
396 surface
->currentDesc
.Width
= width
;
397 surface
->currentDesc
.Height
= height
;
398 surface
->currentDesc
.MultiSampleType
= multisample_type
;
399 surface
->currentDesc
.MultiSampleQuality
= multisample_quality
;
400 surface
->texture_level
= level
;
401 list_init(&surface
->overlays
);
404 surface
->Flags
= SFLAG_NORMCOORD
; /* Default to normalized coords. */
405 if (discard
) surface
->Flags
|= SFLAG_DISCARD
;
406 if (lockable
|| format
== WINED3DFMT_D16_LOCKABLE
) surface
->Flags
|= SFLAG_LOCKABLE
;
408 /* Quick lockable sanity check.
409 * TODO: remove this after surfaces, usage and lockability have been debugged properly
410 * this function is too deep to need to care about things like this.
411 * Levels need to be checked too, since they all affect what can be done. */
414 case WINED3DPOOL_SCRATCH
:
417 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
418 "which are mutually exclusive, setting lockable to TRUE.\n");
423 case WINED3DPOOL_SYSTEMMEM
:
425 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
428 case WINED3DPOOL_MANAGED
:
429 if (usage
& WINED3DUSAGE_DYNAMIC
)
430 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
433 case WINED3DPOOL_DEFAULT
:
434 if (lockable
&& !(usage
& (WINED3DUSAGE_DYNAMIC
| WINED3DUSAGE_RENDERTARGET
| WINED3DUSAGE_DEPTHSTENCIL
)))
435 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
439 FIXME("Unknown pool %#x.\n", pool
);
443 if (usage
& WINED3DUSAGE_RENDERTARGET
&& pool
!= WINED3DPOOL_DEFAULT
)
445 FIXME("Trying to create a render target that isn't in the default pool.\n");
448 /* Mark the texture as dirty so that it gets loaded first time around. */
449 surface_add_dirty_rect(surface
, NULL
);
450 list_init(&surface
->renderbuffers
);
452 TRACE("surface %p, memory %p, size %u\n", surface
, surface
->resource
.allocatedMemory
, surface
->resource
.size
);
454 /* Call the private setup routine */
455 hr
= IWineD3DSurface_PrivateSetup((IWineD3DSurface
*)surface
);
458 ERR("Private setup failed, returning %#x\n", hr
);
466 static void surface_force_reload(IWineD3DSurfaceImpl
*surface
)
468 surface
->Flags
&= ~(SFLAG_ALLOCATED
| SFLAG_SRGBALLOCATED
);
471 void surface_set_texture_name(IWineD3DSurfaceImpl
*surface
, GLuint new_name
, BOOL srgb
)
476 TRACE("surface %p, new_name %u, srgb %#x.\n", surface
, new_name
, srgb
);
480 name
= &surface
->texture_name_srgb
;
481 flag
= SFLAG_INSRGBTEX
;
485 name
= &surface
->texture_name
;
486 flag
= SFLAG_INTEXTURE
;
489 if (!*name
&& new_name
)
491 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
492 * surface has no texture name yet. See if we can get rid of this. */
493 if (surface
->Flags
& flag
)
494 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
495 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, flag
, FALSE
);
499 surface_force_reload(surface
);
502 void surface_set_texture_target(IWineD3DSurfaceImpl
*surface
, GLenum target
)
504 TRACE("surface %p, target %#x.\n", surface
, target
);
506 if (surface
->texture_target
!= target
)
508 if (target
== GL_TEXTURE_RECTANGLE_ARB
)
510 surface
->Flags
&= ~SFLAG_NORMCOORD
;
512 else if (surface
->texture_target
== GL_TEXTURE_RECTANGLE_ARB
)
514 surface
->Flags
|= SFLAG_NORMCOORD
;
517 surface
->texture_target
= target
;
518 surface_force_reload(surface
);
521 /* Context activation is done by the caller. */
522 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl
*This
, BOOL srgb
) {
523 DWORD active_sampler
;
525 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
526 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
527 * gl states. The current texture unit should always be a valid one.
529 * To be more specific, this is tricky because we can implicitly be called
530 * from sampler() in state.c. This means we can't touch anything other than
531 * whatever happens to be the currently active texture, or we would risk
532 * marking already applied sampler states dirty again.
534 * TODO: Track the current active texture per GL context instead of using glGet
536 GLint active_texture
;
538 glGetIntegerv(GL_ACTIVE_TEXTURE
, &active_texture
);
540 active_sampler
= This
->resource
.device
->rev_tex_unit_map
[active_texture
- GL_TEXTURE0_ARB
];
542 if (active_sampler
!= WINED3D_UNMAPPED_STAGE
)
544 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_SAMPLER(active_sampler
));
546 IWineD3DSurface_BindTexture((IWineD3DSurface
*)This
, srgb
);
549 /* This function checks if the primary render target uses the 8bit paletted format. */
550 static BOOL
primary_render_target_is_p8(IWineD3DDeviceImpl
*device
)
552 if (device
->render_targets
&& device
->render_targets
[0])
554 IWineD3DSurfaceImpl
*render_target
= device
->render_targets
[0];
555 if ((render_target
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)
556 && (render_target
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
))
562 /* This call just downloads data, the caller is responsible for binding the
563 * correct texture. */
564 /* Context activation is done by the caller. */
565 static void surface_download_data(IWineD3DSurfaceImpl
*This
, const struct wined3d_gl_info
*gl_info
)
567 const struct wined3d_format_desc
*format_desc
= This
->resource
.format_desc
;
569 /* Only support read back of converted P8 surfaces */
570 if (This
->Flags
& SFLAG_CONVERTED
&& format_desc
->format
!= WINED3DFMT_P8_UINT
)
572 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc
->format
));
578 if (format_desc
->Flags
& WINED3DFMT_FLAG_COMPRESSED
)
580 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
581 This
, This
->texture_level
, format_desc
->glFormat
, format_desc
->glType
,
582 This
->resource
.allocatedMemory
);
584 if (This
->Flags
& SFLAG_PBO
)
586 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, This
->pbo
));
587 checkGLcall("glBindBufferARB");
588 GL_EXTCALL(glGetCompressedTexImageARB(This
->texture_target
, This
->texture_level
, NULL
));
589 checkGLcall("glGetCompressedTexImageARB");
590 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, 0));
591 checkGLcall("glBindBufferARB");
595 GL_EXTCALL(glGetCompressedTexImageARB(This
->texture_target
,
596 This
->texture_level
, This
->resource
.allocatedMemory
));
597 checkGLcall("glGetCompressedTexImageARB");
603 GLenum format
= format_desc
->glFormat
;
604 GLenum type
= format_desc
->glType
;
608 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
609 if (format_desc
->format
== WINED3DFMT_P8_UINT
&& primary_render_target_is_p8(This
->resource
.device
))
612 type
= GL_UNSIGNED_BYTE
;
615 if (This
->Flags
& SFLAG_NONPOW2
) {
616 unsigned char alignment
= This
->resource
.device
->surface_alignment
;
617 src_pitch
= format_desc
->byte_count
* This
->pow2Width
;
618 dst_pitch
= IWineD3DSurface_GetPitch((IWineD3DSurface
*) This
);
619 src_pitch
= (src_pitch
+ alignment
- 1) & ~(alignment
- 1);
620 mem
= HeapAlloc(GetProcessHeap(), 0, src_pitch
* This
->pow2Height
);
622 mem
= This
->resource
.allocatedMemory
;
625 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
626 This
, This
->texture_level
, format
, type
, mem
);
628 if(This
->Flags
& SFLAG_PBO
) {
629 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, This
->pbo
));
630 checkGLcall("glBindBufferARB");
632 glGetTexImage(This
->texture_target
, This
->texture_level
, format
, type
, NULL
);
633 checkGLcall("glGetTexImage");
635 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, 0));
636 checkGLcall("glBindBufferARB");
638 glGetTexImage(This
->texture_target
, This
->texture_level
, format
, type
, mem
);
639 checkGLcall("glGetTexImage");
643 if (This
->Flags
& SFLAG_NONPOW2
) {
644 const BYTE
*src_data
;
648 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
649 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
650 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
652 * We're doing this...
654 * instead of boxing the texture :
655 * |<-texture width ->| -->pow2width| /\
656 * |111111111111111111| | |
657 * |222 Texture 222222| boxed empty | texture height
658 * |3333 Data 33333333| | |
659 * |444444444444444444| | \/
660 * ----------------------------------- |
661 * | boxed empty | boxed empty | pow2height
663 * -----------------------------------
666 * we're repacking the data to the expected texture width
668 * |<-texture width ->| -->pow2width| /\
669 * |111111111111111111222222222222222| |
670 * |222333333333333333333444444444444| texture height
674 * | empty | pow2height
676 * -----------------------------------
680 * |<-texture width ->| /\
681 * |111111111111111111|
682 * |222222222222222222|texture height
683 * |333333333333333333|
684 * |444444444444444444| \/
685 * --------------------
687 * this also means that any references to allocatedMemory should work with the data as if were a
688 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
690 * internally the texture is still stored in a boxed format so any references to textureName will
691 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
693 * Performance should not be an issue, because applications normally do not lock the surfaces when
694 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
695 * and doesn't have to be re-read.
698 dst_data
= This
->resource
.allocatedMemory
;
699 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This
, src_pitch
, dst_pitch
);
700 for (y
= 1 ; y
< This
->currentDesc
.Height
; y
++) {
701 /* skip the first row */
702 src_data
+= src_pitch
;
703 dst_data
+= dst_pitch
;
704 memcpy(dst_data
, src_data
, dst_pitch
);
707 HeapFree(GetProcessHeap(), 0, mem
);
711 /* Surface has now been downloaded */
712 This
->Flags
|= SFLAG_INSYSMEM
;
715 /* This call just uploads data, the caller is responsible for binding the
716 * correct texture. */
717 /* Context activation is done by the caller. */
718 static void surface_upload_data(IWineD3DSurfaceImpl
*This
, const struct wined3d_gl_info
*gl_info
,
719 const struct wined3d_format_desc
*format_desc
, BOOL srgb
, const GLvoid
*data
)
721 GLsizei width
= This
->currentDesc
.Width
;
722 GLsizei height
= This
->currentDesc
.Height
;
727 internal
= format_desc
->glGammaInternal
;
729 else if (This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
&& surface_is_offscreen(This
))
731 internal
= format_desc
->rtInternal
;
735 internal
= format_desc
->glInternal
;
738 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
739 This
, internal
, width
, height
, format_desc
->glFormat
, format_desc
->glType
, data
);
740 TRACE("target %#x, level %u, resource size %u.\n",
741 This
->texture_target
, This
->texture_level
, This
->resource
.size
);
743 if (format_desc
->heightscale
!= 1.0f
&& format_desc
->heightscale
!= 0.0f
) height
*= format_desc
->heightscale
;
747 if (This
->Flags
& SFLAG_PBO
)
749 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
750 checkGLcall("glBindBufferARB");
752 TRACE("(%p) pbo: %#x, data: %p.\n", This
, This
->pbo
, data
);
756 if (format_desc
->Flags
& WINED3DFMT_FLAG_COMPRESSED
)
758 TRACE("Calling glCompressedTexSubImage2DARB.\n");
760 GL_EXTCALL(glCompressedTexSubImage2DARB(This
->texture_target
, This
->texture_level
,
761 0, 0, width
, height
, internal
, This
->resource
.size
, data
));
762 checkGLcall("glCompressedTexSubImage2DARB");
766 TRACE("Calling glTexSubImage2D.\n");
768 glTexSubImage2D(This
->texture_target
, This
->texture_level
,
769 0, 0, width
, height
, format_desc
->glFormat
, format_desc
->glType
, data
);
770 checkGLcall("glTexSubImage2D");
773 if (This
->Flags
& SFLAG_PBO
)
775 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
776 checkGLcall("glBindBufferARB");
781 if (gl_info
->quirks
& WINED3D_QUIRK_FBO_TEX_UPDATE
)
783 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
786 for (i
= 0; i
< device
->numContexts
; ++i
)
788 context_surface_update(device
->contexts
[i
], This
);
793 /* This call just allocates the texture, the caller is responsible for binding
794 * the correct texture. */
795 /* Context activation is done by the caller. */
796 static void surface_allocate_surface(IWineD3DSurfaceImpl
*This
, const struct wined3d_gl_info
*gl_info
,
797 const struct wined3d_format_desc
*format_desc
, BOOL srgb
)
799 BOOL enable_client_storage
= FALSE
;
800 GLsizei width
= This
->pow2Width
;
801 GLsizei height
= This
->pow2Height
;
802 const BYTE
*mem
= NULL
;
807 internal
= format_desc
->glGammaInternal
;
809 else if (This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
&& surface_is_offscreen(This
))
811 internal
= format_desc
->rtInternal
;
815 internal
= format_desc
->glInternal
;
818 if (format_desc
->heightscale
!= 1.0f
&& format_desc
->heightscale
!= 0.0f
) height
*= format_desc
->heightscale
;
820 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",
821 This
, This
->texture_target
, This
->texture_level
, debug_d3dformat(format_desc
->format
),
822 internal
, width
, height
, format_desc
->glFormat
, format_desc
->glType
);
826 if (gl_info
->supported
[APPLE_CLIENT_STORAGE
])
828 if(This
->Flags
& (SFLAG_NONPOW2
| SFLAG_DIBSECTION
| SFLAG_CONVERTED
) || This
->resource
.allocatedMemory
== NULL
) {
829 /* In some cases we want to disable client storage.
830 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
831 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
832 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
833 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
835 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_FALSE
);
836 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
837 This
->Flags
&= ~SFLAG_CLIENT
;
838 enable_client_storage
= TRUE
;
840 This
->Flags
|= SFLAG_CLIENT
;
842 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
843 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
845 mem
= (BYTE
*)(((ULONG_PTR
) This
->resource
.heapMemory
+ (RESOURCE_ALIGNMENT
- 1)) & ~(RESOURCE_ALIGNMENT
- 1));
849 if (format_desc
->Flags
& WINED3DFMT_FLAG_COMPRESSED
&& mem
)
851 GL_EXTCALL(glCompressedTexImage2DARB(This
->texture_target
, This
->texture_level
,
852 internal
, width
, height
, 0, This
->resource
.size
, mem
));
856 glTexImage2D(This
->texture_target
, This
->texture_level
,
857 internal
, width
, height
, 0, format_desc
->glFormat
, format_desc
->glType
, mem
);
858 checkGLcall("glTexImage2D");
861 if(enable_client_storage
) {
862 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_TRUE
);
863 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
868 /* In D3D the depth stencil dimensions have to be greater than or equal to the
869 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
870 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
871 /* GL locking is done by the caller */
872 void surface_set_compatible_renderbuffer(IWineD3DSurfaceImpl
*surface
, unsigned int width
, unsigned int height
)
874 const struct wined3d_gl_info
*gl_info
= &surface
->resource
.device
->adapter
->gl_info
;
875 renderbuffer_entry_t
*entry
;
876 GLuint renderbuffer
= 0;
877 unsigned int src_width
, src_height
;
879 src_width
= surface
->pow2Width
;
880 src_height
= surface
->pow2Height
;
882 /* A depth stencil smaller than the render target is not valid */
883 if (width
> src_width
|| height
> src_height
) return;
885 /* Remove any renderbuffer set if the sizes match */
886 if (gl_info
->supported
[ARB_FRAMEBUFFER_OBJECT
]
887 || (width
== src_width
&& height
== src_height
))
889 surface
->current_renderbuffer
= NULL
;
893 /* Look if we've already got a renderbuffer of the correct dimensions */
894 LIST_FOR_EACH_ENTRY(entry
, &surface
->renderbuffers
, renderbuffer_entry_t
, entry
)
896 if (entry
->width
== width
&& entry
->height
== height
)
898 renderbuffer
= entry
->id
;
899 surface
->current_renderbuffer
= entry
;
906 gl_info
->fbo_ops
.glGenRenderbuffers(1, &renderbuffer
);
907 gl_info
->fbo_ops
.glBindRenderbuffer(GL_RENDERBUFFER
, renderbuffer
);
908 gl_info
->fbo_ops
.glRenderbufferStorage(GL_RENDERBUFFER
,
909 surface
->resource
.format_desc
->glInternal
, width
, height
);
911 entry
= HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t
));
912 entry
->width
= width
;
913 entry
->height
= height
;
914 entry
->id
= renderbuffer
;
915 list_add_head(&surface
->renderbuffers
, &entry
->entry
);
917 surface
->current_renderbuffer
= entry
;
920 checkGLcall("set_compatible_renderbuffer");
923 GLenum
surface_get_gl_buffer(IWineD3DSurfaceImpl
*surface
)
925 IWineD3DSwapChainImpl
*swapchain
= (IWineD3DSwapChainImpl
*)surface
->container
;
927 TRACE("surface %p.\n", surface
);
929 if (!(surface
->Flags
& SFLAG_SWAPCHAIN
))
931 ERR("Surface %p is not on a swapchain.\n", surface
);
935 if (swapchain
->back_buffers
&& swapchain
->back_buffers
[0] == surface
)
937 if (swapchain
->render_to_fbo
)
939 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
940 return GL_COLOR_ATTACHMENT0
;
942 TRACE("Returning GL_BACK\n");
945 else if (surface
== swapchain
->front_buffer
)
947 TRACE("Returning GL_FRONT\n");
951 FIXME("Higher back buffer, returning GL_BACK\n");
955 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
956 void surface_add_dirty_rect(IWineD3DSurfaceImpl
*surface
, const RECT
*dirty_rect
)
958 IWineD3DBaseTexture
*baseTexture
= NULL
;
960 TRACE("surface %p, dirty_rect %s.\n", surface
, wine_dbgstr_rect(dirty_rect
));
962 if (!(surface
->Flags
& SFLAG_INSYSMEM
) && (surface
->Flags
& SFLAG_INTEXTURE
))
963 /* No partial locking for textures yet. */
964 IWineD3DSurface_LoadLocation((IWineD3DSurface
*)surface
, SFLAG_INSYSMEM
, NULL
);
966 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, SFLAG_INSYSMEM
, TRUE
);
969 surface
->dirtyRect
.left
= min(surface
->dirtyRect
.left
, dirty_rect
->left
);
970 surface
->dirtyRect
.top
= min(surface
->dirtyRect
.top
, dirty_rect
->top
);
971 surface
->dirtyRect
.right
= max(surface
->dirtyRect
.right
, dirty_rect
->right
);
972 surface
->dirtyRect
.bottom
= max(surface
->dirtyRect
.bottom
, dirty_rect
->bottom
);
976 surface
->dirtyRect
.left
= 0;
977 surface
->dirtyRect
.top
= 0;
978 surface
->dirtyRect
.right
= surface
->currentDesc
.Width
;
979 surface
->dirtyRect
.bottom
= surface
->currentDesc
.Height
;
982 /* if the container is a basetexture then mark it dirty. */
983 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface
*)surface
,
984 &IID_IWineD3DBaseTexture
, (void **)&baseTexture
)))
986 TRACE("Passing to container\n");
987 IWineD3DBaseTexture_SetDirty(baseTexture
, TRUE
);
988 IWineD3DBaseTexture_Release(baseTexture
);
992 static BOOL
surface_convert_color_to_argb(IWineD3DSurfaceImpl
*This
, DWORD color
, DWORD
*argb_color
)
994 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
996 switch(This
->resource
.format_desc
->format
)
998 case WINED3DFMT_P8_UINT
:
1002 if (primary_render_target_is_p8(device
))
1003 alpha
= color
<< 24;
1007 if (This
->palette
) {
1008 *argb_color
= (alpha
|
1009 (This
->palette
->palents
[color
].peRed
<< 16) |
1010 (This
->palette
->palents
[color
].peGreen
<< 8) |
1011 (This
->palette
->palents
[color
].peBlue
));
1013 *argb_color
= alpha
;
1018 case WINED3DFMT_B5G6R5_UNORM
:
1020 if (color
== 0xFFFF) {
1021 *argb_color
= 0xFFFFFFFF;
1023 *argb_color
= ((0xFF000000) |
1024 ((color
& 0xF800) << 8) |
1025 ((color
& 0x07E0) << 5) |
1026 ((color
& 0x001F) << 3));
1031 case WINED3DFMT_B8G8R8_UNORM
:
1032 case WINED3DFMT_B8G8R8X8_UNORM
:
1033 *argb_color
= 0xFF000000 | color
;
1036 case WINED3DFMT_B8G8R8A8_UNORM
:
1037 *argb_color
= color
;
1041 ERR("Unhandled conversion from %s to ARGB!\n", debug_d3dformat(This
->resource
.format_desc
->format
));
1047 static ULONG WINAPI
IWineD3DSurfaceImpl_Release(IWineD3DSurface
*iface
)
1049 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1050 ULONG ref
= InterlockedDecrement(&This
->resource
.ref
);
1051 TRACE("(%p) : Releasing from %d\n", This
, ref
+ 1);
1055 surface_cleanup(This
);
1056 This
->resource
.parent_ops
->wined3d_object_destroyed(This
->resource
.parent
);
1058 TRACE("(%p) Released.\n", This
);
1059 HeapFree(GetProcessHeap(), 0, This
);
1065 /* ****************************************************
1066 IWineD3DSurface IWineD3DResource parts follow
1067 **************************************************** */
1069 void surface_internal_preload(IWineD3DSurfaceImpl
*surface
, enum WINED3DSRGB srgb
)
1071 /* TODO: check for locks */
1072 IWineD3DDeviceImpl
*device
= surface
->resource
.device
;
1073 IWineD3DBaseTexture
*baseTexture
= NULL
;
1075 TRACE("(%p)Checking to see if the container is a base texture\n", surface
);
1076 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface
*)surface
,
1077 &IID_IWineD3DBaseTexture
, (void **)&baseTexture
)))
1079 IWineD3DBaseTextureImpl
*tex_impl
= (IWineD3DBaseTextureImpl
*)baseTexture
;
1080 TRACE("Passing to container\n");
1081 tex_impl
->baseTexture
.internal_preload(baseTexture
, srgb
);
1082 IWineD3DBaseTexture_Release(baseTexture
);
1084 struct wined3d_context
*context
= NULL
;
1086 TRACE("(%p) : About to load surface\n", surface
);
1088 if (!device
->isInDraw
) context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1090 if (surface
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
1091 || surface
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT_A8_UNORM
)
1093 if (palette9_changed(surface
))
1095 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1096 /* TODO: This is not necessarily needed with hw palettized texture support */
1097 IWineD3DSurface_LoadLocation((IWineD3DSurface
*)surface
, SFLAG_INSYSMEM
, NULL
);
1098 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
1099 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, SFLAG_INTEXTURE
, FALSE
);
1103 IWineD3DSurface_LoadTexture((IWineD3DSurface
*)surface
, srgb
== SRGB_SRGB
? TRUE
: FALSE
);
1105 if (surface
->resource
.pool
== WINED3DPOOL_DEFAULT
)
1107 /* Tell opengl to try and keep this texture in video ram (well mostly) */
1111 glPrioritizeTextures(1, &surface
->texture_name
, &tmp
);
1115 if (context
) context_release(context
);
1119 static void WINAPI
IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface
*iface
)
1121 surface_internal_preload((IWineD3DSurfaceImpl
*)iface
, SRGB_ANY
);
1124 /* Context activation is done by the caller. */
1125 static void surface_remove_pbo(IWineD3DSurfaceImpl
*This
, const struct wined3d_gl_info
*gl_info
)
1127 This
->resource
.heapMemory
= HeapAlloc(GetProcessHeap() ,0 , This
->resource
.size
+ RESOURCE_ALIGNMENT
);
1128 This
->resource
.allocatedMemory
=
1129 (BYTE
*)(((ULONG_PTR
) This
->resource
.heapMemory
+ (RESOURCE_ALIGNMENT
- 1)) & ~(RESOURCE_ALIGNMENT
- 1));
1132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1133 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
1134 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0, This
->resource
.size
, This
->resource
.allocatedMemory
));
1135 checkGLcall("glGetBufferSubDataARB");
1136 GL_EXTCALL(glDeleteBuffersARB(1, &This
->pbo
));
1137 checkGLcall("glDeleteBuffersARB");
1141 This
->Flags
&= ~SFLAG_PBO
;
1144 BOOL
surface_init_sysmem(IWineD3DSurfaceImpl
*surface
)
1146 if (!surface
->resource
.allocatedMemory
)
1148 surface
->resource
.heapMemory
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1149 surface
->resource
.size
+ RESOURCE_ALIGNMENT
);
1150 if (!surface
->resource
.heapMemory
)
1152 ERR("Out of memory\n");
1155 surface
->resource
.allocatedMemory
=
1156 (BYTE
*)(((ULONG_PTR
)surface
->resource
.heapMemory
+ (RESOURCE_ALIGNMENT
- 1)) & ~(RESOURCE_ALIGNMENT
- 1));
1160 memset(surface
->resource
.allocatedMemory
, 0, surface
->resource
.size
);
1163 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, SFLAG_INSYSMEM
, TRUE
);
1167 static void WINAPI
IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface
*iface
) {
1168 IWineD3DBaseTexture
*texture
= NULL
;
1169 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
1170 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1171 const struct wined3d_gl_info
*gl_info
;
1172 renderbuffer_entry_t
*entry
, *entry2
;
1173 struct wined3d_context
*context
;
1175 TRACE("(%p)\n", iface
);
1177 if(This
->resource
.pool
== WINED3DPOOL_DEFAULT
) {
1178 /* Default pool resources are supposed to be destroyed before Reset is called.
1179 * Implicit resources stay however. So this means we have an implicit render target
1180 * or depth stencil. The content may be destroyed, but we still have to tear down
1181 * opengl resources, so we cannot leave early.
1183 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1184 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1185 * or the depth stencil into an FBO the texture or render buffer will be removed
1186 * and all flags get lost
1188 surface_init_sysmem(This
);
1192 /* Load the surface into system memory */
1193 IWineD3DSurface_LoadLocation(iface
, SFLAG_INSYSMEM
, NULL
);
1194 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INDRAWABLE
, FALSE
);
1196 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INTEXTURE
, FALSE
);
1197 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INSRGBTEX
, FALSE
);
1198 This
->Flags
&= ~(SFLAG_ALLOCATED
| SFLAG_SRGBALLOCATED
);
1200 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1201 gl_info
= context
->gl_info
;
1203 /* Destroy PBOs, but load them into real sysmem before */
1204 if (This
->Flags
& SFLAG_PBO
)
1205 surface_remove_pbo(This
, gl_info
);
1207 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1208 * all application-created targets the application has to release the surface
1209 * before calling _Reset
1211 LIST_FOR_EACH_ENTRY_SAFE(entry
, entry2
, &This
->renderbuffers
, renderbuffer_entry_t
, entry
) {
1213 gl_info
->fbo_ops
.glDeleteRenderbuffers(1, &entry
->id
);
1215 list_remove(&entry
->entry
);
1216 HeapFree(GetProcessHeap(), 0, entry
);
1218 list_init(&This
->renderbuffers
);
1219 This
->current_renderbuffer
= NULL
;
1221 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1224 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **) &texture
);
1227 glDeleteTextures(1, &This
->texture_name
);
1228 This
->texture_name
= 0;
1229 glDeleteTextures(1, &This
->texture_name_srgb
);
1230 This
->texture_name_srgb
= 0;
1233 IWineD3DBaseTexture_Release(texture
);
1236 context_release(context
);
1239 /* ******************************************************
1240 IWineD3DSurface IWineD3DSurface parts follow
1241 ****************************************************** */
1243 /* Read the framebuffer back into the surface */
1244 static void read_from_framebuffer(IWineD3DSurfaceImpl
*This
, const RECT
*rect
, void *dest
, UINT pitch
)
1246 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1247 const struct wined3d_gl_info
*gl_info
;
1248 struct wined3d_context
*context
;
1252 BYTE
*row
, *top
, *bottom
;
1256 BOOL srcIsUpsideDown
;
1261 if(wined3d_settings
.rendertargetlock_mode
== RTL_DISABLE
) {
1262 static BOOL warned
= FALSE
;
1264 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1270 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1271 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1272 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1273 * context->last_was_blit set on the unlock.
1275 context
= context_acquire(device
, This
, CTXUSAGE_BLIT
);
1276 gl_info
= context
->gl_info
;
1280 /* Select the correct read buffer, and give some debug output.
1281 * There is no need to keep track of the current read buffer or reset it, every part of the code
1282 * that reads sets the read buffer as desired.
1284 if (surface_is_offscreen(This
))
1286 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1287 * Read from the back buffer
1289 TRACE("Locking offscreen render target\n");
1290 glReadBuffer(device
->offscreenBuffer
);
1291 srcIsUpsideDown
= TRUE
;
1295 /* Onscreen surfaces are always part of a swapchain */
1296 GLenum buffer
= surface_get_gl_buffer(This
);
1297 TRACE("Locking %#x buffer\n", buffer
);
1298 glReadBuffer(buffer
);
1299 checkGLcall("glReadBuffer");
1300 srcIsUpsideDown
= FALSE
;
1303 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1305 local_rect
.left
= 0;
1307 local_rect
.right
= This
->currentDesc
.Width
;
1308 local_rect
.bottom
= This
->currentDesc
.Height
;
1312 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1314 switch(This
->resource
.format_desc
->format
)
1316 case WINED3DFMT_P8_UINT
:
1318 if (primary_render_target_is_p8(device
))
1320 /* In case of P8 render targets the index is stored in the alpha component */
1322 type
= GL_UNSIGNED_BYTE
;
1324 bpp
= This
->resource
.format_desc
->byte_count
;
1326 /* GL can't return palettized data, so read ARGB pixels into a
1327 * separate block of memory and convert them into palettized format
1328 * in software. Slow, but if the app means to use palettized render
1329 * targets and locks it...
1331 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1332 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1333 * for the color channels when palettizing the colors.
1336 type
= GL_UNSIGNED_BYTE
;
1338 mem
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
* 3);
1340 ERR("Out of memory\n");
1344 bpp
= This
->resource
.format_desc
->byte_count
* 3;
1351 fmt
= This
->resource
.format_desc
->glFormat
;
1352 type
= This
->resource
.format_desc
->glType
;
1353 bpp
= This
->resource
.format_desc
->byte_count
;
1356 if(This
->Flags
& SFLAG_PBO
) {
1357 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, This
->pbo
));
1358 checkGLcall("glBindBufferARB");
1360 ERR("mem not null for pbo -- unexpected\n");
1365 /* Save old pixel store pack state */
1366 glGetIntegerv(GL_PACK_ROW_LENGTH
, &rowLen
);
1367 checkGLcall("glGetIntegerv");
1368 glGetIntegerv(GL_PACK_SKIP_PIXELS
, &skipPix
);
1369 checkGLcall("glGetIntegerv");
1370 glGetIntegerv(GL_PACK_SKIP_ROWS
, &skipRow
);
1371 checkGLcall("glGetIntegerv");
1373 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1374 glPixelStorei(GL_PACK_ROW_LENGTH
, This
->currentDesc
.Width
);
1375 checkGLcall("glPixelStorei");
1376 glPixelStorei(GL_PACK_SKIP_PIXELS
, local_rect
.left
);
1377 checkGLcall("glPixelStorei");
1378 glPixelStorei(GL_PACK_SKIP_ROWS
, local_rect
.top
);
1379 checkGLcall("glPixelStorei");
1381 glReadPixels(local_rect
.left
, (!srcIsUpsideDown
) ? (This
->currentDesc
.Height
- local_rect
.bottom
) : local_rect
.top
,
1382 local_rect
.right
- local_rect
.left
,
1383 local_rect
.bottom
- local_rect
.top
,
1385 checkGLcall("glReadPixels");
1387 /* Reset previous pixel store pack state */
1388 glPixelStorei(GL_PACK_ROW_LENGTH
, rowLen
);
1389 checkGLcall("glPixelStorei");
1390 glPixelStorei(GL_PACK_SKIP_PIXELS
, skipPix
);
1391 checkGLcall("glPixelStorei");
1392 glPixelStorei(GL_PACK_SKIP_ROWS
, skipRow
);
1393 checkGLcall("glPixelStorei");
1395 if(This
->Flags
& SFLAG_PBO
) {
1396 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB
, 0));
1397 checkGLcall("glBindBufferARB");
1399 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1400 * to get a pointer to it and perform the flipping in software. This is a lot
1401 * faster than calling glReadPixels for each line. In case we want more speed
1402 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1403 if(!srcIsUpsideDown
) {
1404 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1405 checkGLcall("glBindBufferARB");
1407 mem
= GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, GL_READ_WRITE_ARB
));
1408 checkGLcall("glMapBufferARB");
1412 /* TODO: Merge this with the palettization loop below for P8 targets */
1413 if(!srcIsUpsideDown
) {
1415 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1416 Flip the lines in software */
1417 len
= (local_rect
.right
- local_rect
.left
) * bpp
;
1418 off
= local_rect
.left
* bpp
;
1420 row
= HeapAlloc(GetProcessHeap(), 0, len
);
1422 ERR("Out of memory\n");
1423 if (This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
) HeapFree(GetProcessHeap(), 0, mem
);
1428 top
= mem
+ pitch
* local_rect
.top
;
1429 bottom
= mem
+ pitch
* (local_rect
.bottom
- 1);
1430 for(i
= 0; i
< (local_rect
.bottom
- local_rect
.top
) / 2; i
++) {
1431 memcpy(row
, top
+ off
, len
);
1432 memcpy(top
+ off
, bottom
+ off
, len
);
1433 memcpy(bottom
+ off
, row
, len
);
1437 HeapFree(GetProcessHeap(), 0, row
);
1439 /* Unmap the temp PBO buffer */
1440 if(This
->Flags
& SFLAG_PBO
) {
1441 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
));
1442 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
1447 context_release(context
);
1449 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1450 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1451 * the same color but we have no choice.
1452 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1454 if (This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
&& !primary_render_target_is_p8(device
))
1456 const PALETTEENTRY
*pal
= NULL
;
1457 DWORD width
= pitch
/ 3;
1461 pal
= This
->palette
->palents
;
1463 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1464 HeapFree(GetProcessHeap(), 0, mem
);
1468 for(y
= local_rect
.top
; y
< local_rect
.bottom
; y
++) {
1469 for(x
= local_rect
.left
; x
< local_rect
.right
; x
++) {
1470 /* start lines pixels */
1471 const BYTE
*blue
= mem
+ y
* pitch
+ x
* (sizeof(BYTE
) * 3);
1472 const BYTE
*green
= blue
+ 1;
1473 const BYTE
*red
= green
+ 1;
1475 for(c
= 0; c
< 256; c
++) {
1476 if(*red
== pal
[c
].peRed
&&
1477 *green
== pal
[c
].peGreen
&&
1478 *blue
== pal
[c
].peBlue
)
1480 *((BYTE
*) dest
+ y
* width
+ x
) = c
;
1486 HeapFree(GetProcessHeap(), 0, mem
);
1490 /* Read the framebuffer contents into a texture */
1491 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl
*This
, BOOL srgb
)
1493 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1494 const struct wined3d_gl_info
*gl_info
;
1495 struct wined3d_context
*context
;
1497 BOOL alloc_flag
= srgb
? SFLAG_SRGBALLOCATED
: SFLAG_ALLOCATED
;
1499 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1500 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1501 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1503 context
= context_acquire(device
, This
, CTXUSAGE_RESOURCELOAD
);
1504 gl_info
= context
->gl_info
;
1506 surface_bind_and_dirtify(This
, srgb
);
1509 glGetIntegerv(GL_READ_BUFFER
, &prevRead
);
1512 /* Select the correct read buffer, and give some debug output.
1513 * There is no need to keep track of the current read buffer or reset it, every part of the code
1514 * that reads sets the read buffer as desired.
1516 if (!surface_is_offscreen(This
))
1518 GLenum buffer
= surface_get_gl_buffer(This
);
1519 TRACE("Locking %#x buffer\n", buffer
);
1522 glReadBuffer(buffer
);
1523 checkGLcall("glReadBuffer");
1528 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1529 * Read from the back buffer
1531 TRACE("Locking offscreen render target\n");
1533 glReadBuffer(device
->offscreenBuffer
);
1534 checkGLcall("glReadBuffer");
1538 if (!(This
->Flags
& alloc_flag
))
1540 surface_allocate_surface(This
, gl_info
, This
->resource
.format_desc
, srgb
);
1541 This
->Flags
|= alloc_flag
;
1545 /* If !SrcIsUpsideDown we should flip the surface.
1546 * This can be done using glCopyTexSubImage2D but this
1547 * is VERY slow, so don't do that. We should prevent
1548 * this code from getting called in such cases or perhaps
1549 * we can use FBOs */
1551 glCopyTexSubImage2D(This
->texture_target
, This
->texture_level
,
1552 0, 0, 0, 0, This
->currentDesc
.Width
, This
->currentDesc
.Height
);
1553 checkGLcall("glCopyTexSubImage2D");
1555 glReadBuffer(prevRead
);
1556 checkGLcall("glReadBuffer");
1560 context_release(context
);
1562 TRACE("Updated target %d\n", This
->texture_target
);
1565 /* Context activation is done by the caller. */
1566 static void surface_prepare_texture_internal(IWineD3DSurfaceImpl
*surface
,
1567 const struct wined3d_gl_info
*gl_info
, BOOL srgb
)
1569 DWORD alloc_flag
= srgb
? SFLAG_SRGBALLOCATED
: SFLAG_ALLOCATED
;
1570 CONVERT_TYPES convert
;
1571 struct wined3d_format_desc desc
;
1573 if (surface
->Flags
& alloc_flag
) return;
1575 d3dfmt_get_conv(surface
, TRUE
, TRUE
, &desc
, &convert
);
1576 if(convert
!= NO_CONVERSION
) surface
->Flags
|= SFLAG_CONVERTED
;
1577 else surface
->Flags
&= ~SFLAG_CONVERTED
;
1579 surface_bind_and_dirtify(surface
, srgb
);
1580 surface_allocate_surface(surface
, gl_info
, &desc
, srgb
);
1581 surface
->Flags
|= alloc_flag
;
1584 /* Context activation is done by the caller. */
1585 void surface_prepare_texture(IWineD3DSurfaceImpl
*surface
, const struct wined3d_gl_info
*gl_info
, BOOL srgb
)
1587 IWineD3DBaseTextureImpl
*texture
;
1589 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface
*)surface
,
1590 &IID_IWineD3DBaseTexture
, (void **)&texture
)))
1592 UINT sub_count
= texture
->baseTexture
.level_count
* texture
->baseTexture
.layer_count
;
1595 TRACE("surface %p is a subresource of texture %p.\n", surface
, texture
);
1597 for (i
= 0; i
< sub_count
; ++i
)
1599 IWineD3DSurfaceImpl
*s
= (IWineD3DSurfaceImpl
*)texture
->baseTexture
.sub_resources
[i
];
1600 surface_prepare_texture_internal(s
, gl_info
, srgb
);
1603 IWineD3DBaseTexture_Release((IWineD3DBaseTexture
*)texture
);
1606 surface_prepare_texture_internal(surface
, gl_info
, srgb
);
1609 static void surface_prepare_system_memory(IWineD3DSurfaceImpl
*This
)
1611 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1612 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
1614 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1615 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1618 if(!(This
->Flags
& SFLAG_DYNLOCK
)) {
1620 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1621 if(This
->lockCount
> MAXLOCKCOUNT
) {
1622 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1623 This
->Flags
|= SFLAG_DYNLOCK
;
1627 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1628 * Also don't create a PBO for systemmem surfaces.
1630 if (gl_info
->supported
[ARB_PIXEL_BUFFER_OBJECT
] && (This
->Flags
& SFLAG_DYNLOCK
)
1631 && !(This
->Flags
& (SFLAG_PBO
| SFLAG_CONVERTED
| SFLAG_NONPOW2
))
1632 && (This
->resource
.pool
!= WINED3DPOOL_SYSTEMMEM
))
1635 struct wined3d_context
*context
;
1637 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1640 GL_EXTCALL(glGenBuffersARB(1, &This
->pbo
));
1641 error
= glGetError();
1642 if(This
->pbo
== 0 || error
!= GL_NO_ERROR
) {
1643 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error
), error
);
1646 TRACE("Attaching pbo=%#x to (%p)\n", This
->pbo
, This
);
1648 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1649 checkGLcall("glBindBufferARB");
1651 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->resource
.size
+ 4, This
->resource
.allocatedMemory
, GL_STREAM_DRAW_ARB
));
1652 checkGLcall("glBufferDataARB");
1654 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
1655 checkGLcall("glBindBufferARB");
1657 /* We don't need the system memory anymore and we can't even use it for PBOs */
1658 if(!(This
->Flags
& SFLAG_CLIENT
)) {
1659 HeapFree(GetProcessHeap(), 0, This
->resource
.heapMemory
);
1660 This
->resource
.heapMemory
= NULL
;
1662 This
->resource
.allocatedMemory
= NULL
;
1663 This
->Flags
|= SFLAG_PBO
;
1665 context_release(context
);
1667 else if (!(This
->resource
.allocatedMemory
|| This
->Flags
& SFLAG_PBO
))
1669 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1672 if(!This
->resource
.heapMemory
) {
1673 This
->resource
.heapMemory
= HeapAlloc(GetProcessHeap() ,0 , This
->resource
.size
+ RESOURCE_ALIGNMENT
);
1675 This
->resource
.allocatedMemory
=
1676 (BYTE
*)(((ULONG_PTR
) This
->resource
.heapMemory
+ (RESOURCE_ALIGNMENT
- 1)) & ~(RESOURCE_ALIGNMENT
- 1));
1677 if(This
->Flags
& SFLAG_INSYSMEM
) {
1678 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1683 static HRESULT WINAPI
IWineD3DSurfaceImpl_LockRect(IWineD3DSurface
*iface
, WINED3DLOCKED_RECT
* pLockedRect
, CONST RECT
* pRect
, DWORD Flags
) {
1684 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1685 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1686 const RECT
*pass_rect
= pRect
;
1688 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This
, pRect
, Flags
, pLockedRect
, This
->resource
.allocatedMemory
);
1690 /* This is also done in the base class, but we have to verify this before loading any data from
1691 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1692 * may interfere, and all other bad things may happen
1694 if (This
->Flags
& SFLAG_LOCKED
) {
1695 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1696 return WINED3DERR_INVALIDCALL
;
1698 This
->Flags
|= SFLAG_LOCKED
;
1700 if (!(This
->Flags
& SFLAG_LOCKABLE
))
1702 TRACE("Warning: trying to lock unlockable surf@%p\n", This
);
1705 if (Flags
& WINED3DLOCK_DISCARD
) {
1706 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1707 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1708 surface_prepare_system_memory(This
); /* Makes sure memory is allocated */
1709 This
->Flags
|= SFLAG_INSYSMEM
;
1713 if (This
->Flags
& SFLAG_INSYSMEM
) {
1714 TRACE("Local copy is up to date, not downloading data\n");
1715 surface_prepare_system_memory(This
); /* Makes sure memory is allocated */
1719 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1720 * the full surface. Most callers don't need that, so do it here. */
1721 if (pRect
&& pRect
->top
== 0 && pRect
->left
== 0
1722 && pRect
->right
== This
->currentDesc
.Width
1723 && pRect
->bottom
== This
->currentDesc
.Height
)
1728 if (!(wined3d_settings
.rendertargetlock_mode
== RTL_DISABLE
1729 && ((This
->Flags
& SFLAG_SWAPCHAIN
) || This
== device
->render_targets
[0])))
1731 IWineD3DSurface_LoadLocation(iface
, SFLAG_INSYSMEM
, pass_rect
);
1735 if (This
->Flags
& SFLAG_PBO
)
1737 const struct wined3d_gl_info
*gl_info
;
1738 struct wined3d_context
*context
;
1740 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1741 gl_info
= context
->gl_info
;
1744 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1745 checkGLcall("glBindBufferARB");
1747 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1748 if(This
->resource
.allocatedMemory
) {
1749 ERR("The surface already has PBO memory allocated!\n");
1752 This
->resource
.allocatedMemory
= GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, GL_READ_WRITE_ARB
));
1753 checkGLcall("glMapBufferARB");
1755 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1756 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
1757 checkGLcall("glBindBufferARB");
1760 context_release(context
);
1763 if (Flags
& (WINED3DLOCK_NO_DIRTY_UPDATE
| WINED3DLOCK_READONLY
)) {
1766 IWineD3DBaseTexture
*pBaseTexture
;
1769 * as seen in msdn docs
1771 surface_add_dirty_rect(This
, pRect
);
1773 /** Dirtify Container if needed */
1774 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&pBaseTexture
))) {
1775 TRACE("Making container dirty\n");
1776 IWineD3DBaseTexture_SetDirty(pBaseTexture
, TRUE
);
1777 IWineD3DBaseTexture_Release(pBaseTexture
);
1779 TRACE("Surface is standalone, no need to dirty the container\n");
1783 return IWineD3DBaseSurfaceImpl_LockRect(iface
, pLockedRect
, pRect
, Flags
);
1786 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl
*This
, GLenum fmt
, GLenum type
, UINT bpp
, const BYTE
*mem
) {
1788 GLint prev_rasterpos
[4];
1789 GLint skipBytes
= 0;
1790 UINT pitch
= IWineD3DSurface_GetPitch((IWineD3DSurface
*) This
); /* target is argb, 4 byte */
1791 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1792 const struct wined3d_gl_info
*gl_info
;
1793 struct wined3d_context
*context
;
1795 /* Activate the correct context for the render target */
1796 context
= context_acquire(device
, This
, CTXUSAGE_BLIT
);
1797 gl_info
= context
->gl_info
;
1801 if (!surface_is_offscreen(This
))
1803 GLenum buffer
= surface_get_gl_buffer(This
);
1804 TRACE("Unlocking %#x buffer.\n", buffer
);
1805 context_set_draw_buffer(context
, buffer
);
1809 /* Primary offscreen render target */
1810 TRACE("Offscreen render target.\n");
1811 context_set_draw_buffer(context
, device
->offscreenBuffer
);
1814 glGetIntegerv(GL_PACK_SWAP_BYTES
, &prev_store
);
1815 checkGLcall("glGetIntegerv");
1816 glGetIntegerv(GL_CURRENT_RASTER_POSITION
, &prev_rasterpos
[0]);
1817 checkGLcall("glGetIntegerv");
1818 glPixelZoom(1.0f
, -1.0f
);
1819 checkGLcall("glPixelZoom");
1821 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1822 glGetIntegerv(GL_UNPACK_ROW_LENGTH
, &skipBytes
);
1823 glPixelStorei(GL_UNPACK_ROW_LENGTH
, This
->currentDesc
.Width
);
1825 glRasterPos3i(This
->lockedRect
.left
, This
->lockedRect
.top
, 1);
1826 checkGLcall("glRasterPos3i");
1828 /* Some drivers(radeon dri, others?) don't like exceptions during
1829 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1830 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1831 * catch to put the dib section in InSync mode, which leads to a crash
1832 * and a blocked x server on my radeon card.
1834 * The following lines read the dib section so it is put in InSync mode
1835 * before glDrawPixels is called and the crash is prevented. There won't
1836 * be any interfering gdi accesses, because UnlockRect is called from
1837 * ReleaseDC, and the app won't use the dc any more afterwards.
1839 if((This
->Flags
& SFLAG_DIBSECTION
) && !(This
->Flags
& SFLAG_PBO
)) {
1841 read
= This
->resource
.allocatedMemory
[0];
1844 if(This
->Flags
& SFLAG_PBO
) {
1845 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1846 checkGLcall("glBindBufferARB");
1849 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1850 if(This
->Flags
& SFLAG_LOCKED
) {
1851 glDrawPixels(This
->lockedRect
.right
- This
->lockedRect
.left
,
1852 (This
->lockedRect
.bottom
- This
->lockedRect
.top
)-1,
1854 mem
+ bpp
* This
->lockedRect
.left
+ pitch
* This
->lockedRect
.top
);
1855 checkGLcall("glDrawPixels");
1857 glDrawPixels(This
->currentDesc
.Width
,
1858 This
->currentDesc
.Height
,
1860 checkGLcall("glDrawPixels");
1863 if(This
->Flags
& SFLAG_PBO
) {
1864 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
1865 checkGLcall("glBindBufferARB");
1868 glPixelZoom(1.0f
, 1.0f
);
1869 checkGLcall("glPixelZoom");
1871 glRasterPos3iv(&prev_rasterpos
[0]);
1872 checkGLcall("glRasterPos3iv");
1874 /* Reset to previous pack row length */
1875 glPixelStorei(GL_UNPACK_ROW_LENGTH
, skipBytes
);
1876 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1879 context_release(context
);
1882 static HRESULT WINAPI
IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface
*iface
) {
1883 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1884 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1887 if (!(This
->Flags
& SFLAG_LOCKED
)) {
1888 WARN("trying to Unlock an unlocked surf@%p\n", This
);
1889 return WINEDDERR_NOTLOCKED
;
1892 if (This
->Flags
& SFLAG_PBO
)
1894 const struct wined3d_gl_info
*gl_info
;
1895 struct wined3d_context
*context
;
1897 TRACE("Freeing PBO memory\n");
1899 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1900 gl_info
= context
->gl_info
;
1903 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, This
->pbo
));
1904 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
));
1905 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB
, 0));
1906 checkGLcall("glUnmapBufferARB");
1908 context_release(context
);
1910 This
->resource
.allocatedMemory
= NULL
;
1913 TRACE("(%p) : dirtyfied(%d)\n", This
, This
->Flags
& (SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
) ? 0 : 1);
1915 if (This
->Flags
& (SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
)) {
1916 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This
);
1920 if ((This
->Flags
& SFLAG_SWAPCHAIN
) || (device
->render_targets
&& This
== device
->render_targets
[0]))
1922 if(wined3d_settings
.rendertargetlock_mode
== RTL_DISABLE
) {
1923 static BOOL warned
= FALSE
;
1925 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1931 if(This
->dirtyRect
.left
== 0 &&
1932 This
->dirtyRect
.top
== 0 &&
1933 This
->dirtyRect
.right
== This
->currentDesc
.Width
&&
1934 This
->dirtyRect
.bottom
== This
->currentDesc
.Height
) {
1937 /* TODO: Proper partial rectangle tracking */
1938 fullsurface
= FALSE
;
1939 This
->Flags
|= SFLAG_INSYSMEM
;
1942 switch(wined3d_settings
.rendertargetlock_mode
) {
1944 IWineD3DSurface_LoadLocation(iface
, SFLAG_INTEXTURE
, NULL
/* partial texture loading not supported yet */);
1948 IWineD3DSurface_LoadLocation(iface
, SFLAG_INDRAWABLE
, fullsurface
? NULL
: &This
->dirtyRect
);
1953 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1954 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1955 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1956 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1957 * not fully up to date because only a subrectangle was read in LockRect.
1959 This
->Flags
&= ~SFLAG_INSYSMEM
;
1960 This
->Flags
|= SFLAG_INDRAWABLE
;
1963 This
->dirtyRect
.left
= This
->currentDesc
.Width
;
1964 This
->dirtyRect
.top
= This
->currentDesc
.Height
;
1965 This
->dirtyRect
.right
= 0;
1966 This
->dirtyRect
.bottom
= 0;
1968 else if (This
== device
->depth_stencil
)
1970 FIXME("Depth Stencil buffer locking is not implemented\n");
1972 /* The rest should be a normal texture */
1973 IWineD3DBaseTextureImpl
*impl
;
1974 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1975 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1976 * states need resetting
1978 if(IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&impl
) == WINED3D_OK
) {
1979 if (impl
->baseTexture
.bindCount
)
1980 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_SAMPLER(impl
->baseTexture
.sampler
));
1981 IWineD3DBaseTexture_Release((IWineD3DBaseTexture
*) impl
);
1986 This
->Flags
&= ~SFLAG_LOCKED
;
1987 memset(&This
->lockedRect
, 0, sizeof(RECT
));
1989 /* Overlays have to be redrawn manually after changes with the GL implementation */
1990 if(This
->overlay_dest
) {
1991 IWineD3DSurface_DrawOverlay(iface
);
1996 static void surface_release_client_storage(IWineD3DSurfaceImpl
*surface
)
1998 struct wined3d_context
*context
;
2000 context
= context_acquire(surface
->resource
.device
, NULL
, CTXUSAGE_RESOURCELOAD
);
2003 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_FALSE
);
2004 if (surface
->texture_name
)
2006 surface_bind_and_dirtify(surface
, FALSE
);
2007 glTexImage2D(surface
->texture_target
, surface
->texture_level
,
2008 GL_RGB
, 1, 1, 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2010 if (surface
->texture_name_srgb
)
2012 surface_bind_and_dirtify(surface
, TRUE
);
2013 glTexImage2D(surface
->texture_target
, surface
->texture_level
,
2014 GL_RGB
, 1, 1, 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2016 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_TRUE
);
2019 context_release(context
);
2021 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, SFLAG_INSRGBTEX
, FALSE
);
2022 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)surface
, SFLAG_INTEXTURE
, FALSE
);
2023 surface_force_reload(surface
);
2026 static HRESULT WINAPI
IWineD3DSurfaceImpl_GetDC(IWineD3DSurface
*iface
, HDC
*pHDC
)
2028 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2029 WINED3DLOCKED_RECT lock
;
2033 TRACE("(%p)->(%p)\n",This
,pHDC
);
2035 if(This
->Flags
& SFLAG_USERPTR
) {
2036 ERR("Not supported on surfaces with an application-provided surfaces\n");
2037 return WINEDDERR_NODC
;
2040 /* Give more detailed info for ddraw */
2041 if (This
->Flags
& SFLAG_DCINUSE
)
2042 return WINEDDERR_DCALREADYCREATED
;
2044 /* Can't GetDC if the surface is locked */
2045 if (This
->Flags
& SFLAG_LOCKED
)
2046 return WINED3DERR_INVALIDCALL
;
2048 memset(&lock
, 0, sizeof(lock
)); /* To be sure */
2050 /* Create a DIB section if there isn't a hdc yet */
2052 if(This
->Flags
& SFLAG_CLIENT
) {
2053 IWineD3DSurface_LoadLocation(iface
, SFLAG_INSYSMEM
, NULL
);
2054 surface_release_client_storage(This
);
2056 hr
= IWineD3DBaseSurfaceImpl_CreateDIBSection(iface
);
2057 if(FAILED(hr
)) return WINED3DERR_INVALIDCALL
;
2059 /* Use the dib section from now on if we are not using a PBO */
2060 if(!(This
->Flags
& SFLAG_PBO
))
2061 This
->resource
.allocatedMemory
= This
->dib
.bitmap_data
;
2064 /* Lock the surface */
2065 hr
= IWineD3DSurface_LockRect(iface
,
2070 if(This
->Flags
& SFLAG_PBO
) {
2071 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
2072 memcpy(This
->dib
.bitmap_data
, This
->resource
.allocatedMemory
, This
->dib
.bitmap_size
);
2076 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr
);
2077 /* keep the dib section */
2081 if (This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
2082 || This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT_A8_UNORM
)
2084 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
2085 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
2087 const PALETTEENTRY
*pal
= NULL
;
2090 pal
= This
->palette
->palents
;
2092 IWineD3DSurfaceImpl
*dds_primary
;
2093 IWineD3DSwapChainImpl
*swapchain
;
2094 swapchain
= (IWineD3DSwapChainImpl
*)This
->resource
.device
->swapchains
[0];
2095 dds_primary
= swapchain
->front_buffer
;
2096 if (dds_primary
&& dds_primary
->palette
)
2097 pal
= dds_primary
->palette
->palents
;
2101 for (n
=0; n
<256; n
++) {
2102 col
[n
].rgbRed
= pal
[n
].peRed
;
2103 col
[n
].rgbGreen
= pal
[n
].peGreen
;
2104 col
[n
].rgbBlue
= pal
[n
].peBlue
;
2105 col
[n
].rgbReserved
= 0;
2107 SetDIBColorTable(This
->hDC
, 0, 256, col
);
2112 TRACE("returning %p\n",*pHDC
);
2113 This
->Flags
|= SFLAG_DCINUSE
;
2118 static HRESULT WINAPI
IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface
*iface
, HDC hDC
)
2120 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2122 TRACE("(%p)->(%p)\n",This
,hDC
);
2124 if (!(This
->Flags
& SFLAG_DCINUSE
))
2125 return WINEDDERR_NODC
;
2127 if (This
->hDC
!=hDC
) {
2128 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC
, This
->hDC
);
2129 return WINEDDERR_NODC
;
2132 if((This
->Flags
& SFLAG_PBO
) && This
->resource
.allocatedMemory
) {
2133 /* Copy the contents of the DIB over to the PBO */
2134 memcpy(This
->resource
.allocatedMemory
, This
->dib
.bitmap_data
, This
->dib
.bitmap_size
);
2137 /* we locked first, so unlock now */
2138 IWineD3DSurface_UnlockRect(iface
);
2140 This
->Flags
&= ~SFLAG_DCINUSE
;
2145 /* ******************************************************
2146 IWineD3DSurface Internal (No mapping to directx api) parts follow
2147 ****************************************************** */
2149 HRESULT
d3dfmt_get_conv(IWineD3DSurfaceImpl
*This
, BOOL need_alpha_ck
, BOOL use_texturing
, struct wined3d_format_desc
*desc
, CONVERT_TYPES
*convert
)
2151 BOOL colorkey_active
= need_alpha_ck
&& (This
->CKeyFlags
& WINEDDSD_CKSRCBLT
);
2152 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
2153 BOOL blit_supported
= FALSE
;
2154 RECT rect
= {0, 0, This
->pow2Width
, This
->pow2Height
};
2156 /* Copy the default values from the surface. Below we might perform fixups */
2157 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2158 *desc
= *This
->resource
.format_desc
;
2159 *convert
= NO_CONVERSION
;
2161 /* Ok, now look if we have to do any conversion */
2162 switch(This
->resource
.format_desc
->format
)
2164 case WINED3DFMT_P8_UINT
:
2169 blit_supported
= device
->blitter
->blit_supported(&device
->adapter
->gl_info
, BLIT_OP_BLIT
,
2170 &rect
, This
->resource
.usage
, This
->resource
.pool
,
2171 This
->resource
.format_desc
, &rect
, This
->resource
.usage
,
2172 This
->resource
.pool
, This
->resource
.format_desc
);
2174 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2175 * texturing. Further also use conversion in case of color keying.
2176 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2177 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2178 * conflicts with this.
2180 if (!((blit_supported
&& device
->render_targets
&& This
== device
->render_targets
[0]))
2181 || colorkey_active
|| !use_texturing
)
2183 desc
->glFormat
= GL_RGBA
;
2184 desc
->glInternal
= GL_RGBA
;
2185 desc
->glType
= GL_UNSIGNED_BYTE
;
2186 desc
->conv_byte_count
= 4;
2187 if(colorkey_active
) {
2188 *convert
= CONVERT_PALETTED_CK
;
2190 *convert
= CONVERT_PALETTED
;
2195 case WINED3DFMT_B2G3R3_UNORM
:
2196 /* **********************
2197 GL_UNSIGNED_BYTE_3_3_2
2198 ********************** */
2199 if (colorkey_active
) {
2200 /* This texture format will never be used.. So do not care about color keying
2201 up until the point in time it will be needed :-) */
2202 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2206 case WINED3DFMT_B5G6R5_UNORM
:
2207 if (colorkey_active
) {
2208 *convert
= CONVERT_CK_565
;
2209 desc
->glFormat
= GL_RGBA
;
2210 desc
->glInternal
= GL_RGB5_A1
;
2211 desc
->glType
= GL_UNSIGNED_SHORT_5_5_5_1
;
2212 desc
->conv_byte_count
= 2;
2216 case WINED3DFMT_B5G5R5X1_UNORM
:
2217 if (colorkey_active
) {
2218 *convert
= CONVERT_CK_5551
;
2219 desc
->glFormat
= GL_BGRA
;
2220 desc
->glInternal
= GL_RGB5_A1
;
2221 desc
->glType
= GL_UNSIGNED_SHORT_1_5_5_5_REV
;
2222 desc
->conv_byte_count
= 2;
2226 case WINED3DFMT_B8G8R8_UNORM
:
2227 if (colorkey_active
) {
2228 *convert
= CONVERT_CK_RGB24
;
2229 desc
->glFormat
= GL_RGBA
;
2230 desc
->glInternal
= GL_RGBA8
;
2231 desc
->glType
= GL_UNSIGNED_INT_8_8_8_8
;
2232 desc
->conv_byte_count
= 4;
2236 case WINED3DFMT_B8G8R8X8_UNORM
:
2237 if (colorkey_active
) {
2238 *convert
= CONVERT_RGB32_888
;
2239 desc
->glFormat
= GL_RGBA
;
2240 desc
->glInternal
= GL_RGBA8
;
2241 desc
->glType
= GL_UNSIGNED_INT_8_8_8_8
;
2242 desc
->conv_byte_count
= 4;
2253 void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl
*This
, BYTE table
[256][4], BOOL colorkey
)
2255 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
2256 IWineD3DPaletteImpl
*pal
= This
->palette
;
2257 BOOL index_in_alpha
= FALSE
;
2260 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2261 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2262 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2263 * duplicate entries. Store the color key in the unused alpha component to speed the
2264 * download up and to make conversion unneeded. */
2265 index_in_alpha
= primary_render_target_is_p8(device
);
2269 UINT dxVersion
= ((IWineD3DImpl
*)device
->wined3d
)->dxVersion
;
2271 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2274 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2277 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2278 * there's no palette at this time. */
2279 for (i
= 0; i
< 256; i
++) table
[i
][3] = i
;
2284 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2285 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2286 * capability flag is present (wine does advertise this capability) */
2287 for (i
= 0; i
< 256; ++i
)
2289 table
[i
][0] = device
->palettes
[device
->currentPalette
][i
].peRed
;
2290 table
[i
][1] = device
->palettes
[device
->currentPalette
][i
].peGreen
;
2291 table
[i
][2] = device
->palettes
[device
->currentPalette
][i
].peBlue
;
2292 table
[i
][3] = device
->palettes
[device
->currentPalette
][i
].peFlags
;
2298 TRACE("Using surface palette %p\n", pal
);
2299 /* Get the surface's palette */
2300 for (i
= 0; i
< 256; ++i
)
2302 table
[i
][0] = pal
->palents
[i
].peRed
;
2303 table
[i
][1] = pal
->palents
[i
].peGreen
;
2304 table
[i
][2] = pal
->palents
[i
].peBlue
;
2306 /* When index_in_alpha is set the palette index is stored in the
2307 * alpha component. In case of a readback we can then read
2308 * GL_ALPHA. Color keying is handled in BltOverride using a
2309 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2310 * color key itself is passed to glAlphaFunc in other cases the
2311 * alpha component of pixels that should be masked away is set to 0. */
2316 else if (colorkey
&& (i
>= This
->SrcBltCKey
.dwColorSpaceLowValue
)
2317 && (i
<= This
->SrcBltCKey
.dwColorSpaceHighValue
))
2321 else if(pal
->Flags
& WINEDDPCAPS_ALPHA
)
2323 table
[i
][3] = pal
->palents
[i
].peFlags
;
2333 static HRESULT
d3dfmt_convert_surface(const BYTE
*src
, BYTE
*dst
, UINT pitch
, UINT width
,
2334 UINT height
, UINT outpitch
, CONVERT_TYPES convert
, IWineD3DSurfaceImpl
*This
)
2338 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src
, dst
, pitch
, height
, outpitch
, convert
,This
);
2343 memcpy(dst
, src
, pitch
* height
);
2346 case CONVERT_PALETTED
:
2347 case CONVERT_PALETTED_CK
:
2349 IWineD3DPaletteImpl
* pal
= This
->palette
;
2354 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2357 d3dfmt_p8_init_palette(This
, table
, (convert
== CONVERT_PALETTED_CK
));
2359 for (y
= 0; y
< height
; y
++)
2361 source
= src
+ pitch
* y
;
2362 dest
= dst
+ outpitch
* y
;
2363 /* This is an 1 bpp format, using the width here is fine */
2364 for (x
= 0; x
< width
; x
++) {
2365 BYTE color
= *source
++;
2366 *dest
++ = table
[color
][0];
2367 *dest
++ = table
[color
][1];
2368 *dest
++ = table
[color
][2];
2369 *dest
++ = table
[color
][3];
2375 case CONVERT_CK_565
:
2377 /* Converting the 565 format in 5551 packed to emulate color-keying.
2379 Note : in all these conversion, it would be best to average the averaging
2380 pixels to get the color of the pixel that will be color-keyed to
2381 prevent 'color bleeding'. This will be done later on if ever it is
2384 Note2: Nvidia documents say that their driver does not support alpha + color keying
2385 on the same surface and disables color keying in such a case
2391 TRACE("Color keyed 565\n");
2393 for (y
= 0; y
< height
; y
++) {
2394 Source
= (const WORD
*)(src
+ y
* pitch
);
2395 Dest
= (WORD
*) (dst
+ y
* outpitch
);
2396 for (x
= 0; x
< width
; x
++ ) {
2397 WORD color
= *Source
++;
2398 *Dest
= ((color
& 0xFFC0) | ((color
& 0x1F) << 1));
2399 if ((color
< This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2400 (color
> This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
2409 case CONVERT_CK_5551
:
2411 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2415 TRACE("Color keyed 5551\n");
2416 for (y
= 0; y
< height
; y
++) {
2417 Source
= (const WORD
*)(src
+ y
* pitch
);
2418 Dest
= (WORD
*) (dst
+ y
* outpitch
);
2419 for (x
= 0; x
< width
; x
++ ) {
2420 WORD color
= *Source
++;
2422 if ((color
< This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2423 (color
> This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
2427 *Dest
&= ~(1 << 15);
2435 case CONVERT_CK_RGB24
:
2437 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2439 for (y
= 0; y
< height
; y
++)
2441 source
= src
+ pitch
* y
;
2442 dest
= dst
+ outpitch
* y
;
2443 for (x
= 0; x
< width
; x
++) {
2444 DWORD color
= ((DWORD
)source
[0] << 16) + ((DWORD
)source
[1] << 8) + (DWORD
)source
[2] ;
2445 DWORD dstcolor
= color
<< 8;
2446 if ((color
< This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2447 (color
> This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
2450 *(DWORD
*)dest
= dstcolor
;
2458 case CONVERT_RGB32_888
:
2460 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2462 for (y
= 0; y
< height
; y
++)
2464 source
= src
+ pitch
* y
;
2465 dest
= dst
+ outpitch
* y
;
2466 for (x
= 0; x
< width
; x
++) {
2467 DWORD color
= 0xffffff & *(const DWORD
*)source
;
2468 DWORD dstcolor
= color
<< 8;
2469 if ((color
< This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2470 (color
> This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
2473 *(DWORD
*)dest
= dstcolor
;
2482 ERR("Unsupported conversion type %#x.\n", convert
);
2487 BOOL
palette9_changed(IWineD3DSurfaceImpl
*This
)
2489 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
2491 if (This
->palette
|| (This
->resource
.format_desc
->format
!= WINED3DFMT_P8_UINT
2492 && This
->resource
.format_desc
->format
!= WINED3DFMT_P8_UINT_A8_UNORM
))
2494 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2495 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2502 if (!memcmp(This
->palette9
, device
->palettes
[device
->currentPalette
], sizeof(PALETTEENTRY
) * 256))
2507 This
->palette9
= HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY
) * 256);
2509 memcpy(This
->palette9
, device
->palettes
[device
->currentPalette
], sizeof(PALETTEENTRY
) * 256);
2513 static HRESULT WINAPI
IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface
*iface
, BOOL srgb_mode
) {
2514 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2515 DWORD flag
= srgb_mode
? SFLAG_INSRGBTEX
: SFLAG_INTEXTURE
;
2517 if (!(This
->Flags
& flag
)) {
2518 TRACE("Reloading because surface is dirty\n");
2519 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2520 ((This
->Flags
& SFLAG_GLCKEY
) && (!(This
->CKeyFlags
& WINEDDSD_CKSRCBLT
))) ||
2521 /* Reload: vice versa OR */
2522 ((!(This
->Flags
& SFLAG_GLCKEY
)) && (This
->CKeyFlags
& WINEDDSD_CKSRCBLT
)) ||
2523 /* Also reload: Color key is active AND the color key has changed */
2524 ((This
->CKeyFlags
& WINEDDSD_CKSRCBLT
) && (
2525 (This
->glCKey
.dwColorSpaceLowValue
!= This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2526 (This
->glCKey
.dwColorSpaceHighValue
!= This
->SrcBltCKey
.dwColorSpaceHighValue
)))) {
2527 TRACE("Reloading because of color keying\n");
2528 /* To perform the color key conversion we need a sysmem copy of
2529 * the surface. Make sure we have it
2532 IWineD3DSurface_LoadLocation(iface
, SFLAG_INSYSMEM
, NULL
);
2533 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2534 /* TODO: This is not necessarily needed with hw palettized texture support */
2535 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INSYSMEM
, TRUE
);
2537 TRACE("surface is already in texture\n");
2541 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2542 * These resources are not bound by device size or format restrictions. Because of this,
2543 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2544 * However, these resources can always be created, locked, and copied.
2546 if (This
->resource
.pool
== WINED3DPOOL_SCRATCH
)
2548 FIXME("(%p) Operation not supported for scratch textures\n",This
);
2549 return WINED3DERR_INVALIDCALL
;
2552 IWineD3DSurface_LoadLocation(iface
, flag
, NULL
/* no partial locking for textures yet */);
2556 static unsigned int gen
= 0;
2559 if ((gen
% 10) == 0) {
2560 snprintf(buffer
, sizeof(buffer
), "/tmp/surface%p_type%u_level%u_%u.ppm",
2561 This
, This
->texture_target
, This
->texture_level
, gen
);
2562 IWineD3DSurfaceImpl_SaveSnapshot(iface
, buffer
);
2565 * debugging crash code
2574 if (!(This
->Flags
& SFLAG_DONOTFREE
)) {
2575 HeapFree(GetProcessHeap(), 0, This
->resource
.heapMemory
);
2576 This
->resource
.allocatedMemory
= NULL
;
2577 This
->resource
.heapMemory
= NULL
;
2578 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INSYSMEM
, FALSE
);
2584 /* Context activation is done by the caller. */
2585 static void WINAPI
IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface
*iface
, BOOL srgb
) {
2586 /* TODO: check for locks */
2587 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2588 IWineD3DBaseTexture
*baseTexture
= NULL
;
2590 TRACE("(%p)Checking to see if the container is a base texture\n", This
);
2591 if (IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&baseTexture
) == WINED3D_OK
) {
2592 TRACE("Passing to container\n");
2593 IWineD3DBaseTexture_BindTexture(baseTexture
, srgb
);
2594 IWineD3DBaseTexture_Release(baseTexture
);
2600 TRACE("(%p) : Binding surface\n", This
);
2602 name
= srgb
? &This
->texture_name_srgb
: &This
->texture_name
;
2606 if (!This
->texture_level
)
2609 glGenTextures(1, name
);
2610 checkGLcall("glGenTextures");
2611 TRACE("Surface %p given name %d\n", This
, *name
);
2613 glBindTexture(This
->texture_target
, *name
);
2614 checkGLcall("glBindTexture");
2615 glTexParameteri(This
->texture_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
2616 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2617 glTexParameteri(This
->texture_target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
2618 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2619 glTexParameteri(This
->texture_target
, GL_TEXTURE_WRAP_R
, GL_CLAMP_TO_EDGE
);
2620 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2621 glTexParameteri(This
->texture_target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
2622 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2623 glTexParameteri(This
->texture_target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
2624 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2626 /* This is where we should be reducing the amount of GLMemoryUsed */
2628 /* Mipmap surfaces should have a base texture container */
2629 ERR("Mipmap surface has a glTexture bound to it!\n");
2632 glBindTexture(This
->texture_target
, *name
);
2633 checkGLcall("glBindTexture");
2641 static HRESULT WINAPI
IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface
*iface
, const char* filename
)
2644 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2645 char *allocatedMemory
;
2646 const char *textureRow
;
2647 IWineD3DSwapChain
*swapChain
= NULL
;
2648 int width
, height
, i
, y
;
2649 GLuint tmpTexture
= 0;
2652 Textures may not be stored in ->allocatedgMemory and a GlTexture
2653 so we should lock the surface before saving a snapshot, or at least check that
2655 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2656 by calling GetTexImage and in compressed form by calling
2657 GetCompressedTexImageARB. Queried compressed images can be saved and
2658 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2659 texture images do not need to be processed by the GL and should
2660 significantly improve texture loading performance relative to uncompressed
2663 /* Setup the width and height to be the internal texture width and height. */
2664 width
= This
->pow2Width
;
2665 height
= This
->pow2Height
;
2666 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2667 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **)&swapChain
);
2669 if (This
->Flags
& SFLAG_INDRAWABLE
&& !(This
->Flags
& SFLAG_INTEXTURE
)) {
2670 /* if were not a real texture then read the back buffer into a real texture */
2671 /* we don't want to interfere with the back buffer so read the data into a temporary
2672 * texture and then save the data out of the temporary texture
2676 TRACE("(%p) Reading render target into texture\n", This
);
2678 glGenTextures(1, &tmpTexture
);
2679 glBindTexture(GL_TEXTURE_2D
, tmpTexture
);
2681 glTexImage2D(GL_TEXTURE_2D
,
2688 GL_UNSIGNED_INT_8_8_8_8_REV
,
2691 glGetIntegerv(GL_READ_BUFFER
, &prevRead
);
2692 checkGLcall("glGetIntegerv");
2693 glReadBuffer(swapChain
? GL_BACK
: This
->resource
.device
->offscreenBuffer
);
2694 checkGLcall("glReadBuffer");
2695 glCopyTexImage2D(GL_TEXTURE_2D
,
2704 checkGLcall("glCopyTexImage2D");
2705 glReadBuffer(prevRead
);
2708 } else { /* bind the real texture, and make sure it up to date */
2709 surface_internal_preload(This
, SRGB_RGB
);
2710 surface_bind_and_dirtify(This
, FALSE
);
2712 allocatedMemory
= HeapAlloc(GetProcessHeap(), 0, width
* height
* 4);
2714 FIXME("Saving texture level %d width %d height %d\n", This
->texture_level
, width
, height
);
2715 glGetTexImage(GL_TEXTURE_2D
, This
->texture_level
, GL_RGBA
, GL_UNSIGNED_INT_8_8_8_8_REV
, allocatedMemory
);
2716 checkGLcall("glGetTexImage");
2718 glBindTexture(GL_TEXTURE_2D
, 0);
2719 glDeleteTextures(1, &tmpTexture
);
2723 f
= fopen(filename
, "w+");
2725 ERR("opening of %s failed with: %s\n", filename
, strerror(errno
));
2726 return WINED3DERR_INVALIDCALL
;
2728 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2729 TRACE("(%p) opened %s with format %s\n", This
, filename
, debug_d3dformat(This
->resource
.format_desc
->format
));
2744 fwrite(&width
,2,1,f
);
2746 fwrite(&height
,2,1,f
);
2751 /* 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 */
2753 textureRow
= allocatedMemory
+ (width
* (height
- 1) *4);
2755 textureRow
= allocatedMemory
;
2756 for (y
= 0 ; y
< height
; y
++) {
2757 for (i
= 0; i
< width
; i
++) {
2758 color
= *((const DWORD
*)textureRow
);
2759 fputc((color
>> 16) & 0xFF, f
); /* B */
2760 fputc((color
>> 8) & 0xFF, f
); /* G */
2761 fputc((color
>> 0) & 0xFF, f
); /* R */
2762 fputc((color
>> 24) & 0xFF, f
); /* A */
2765 /* take two rows of the pointer to the texture memory */
2767 (textureRow
-= width
<< 3);
2770 TRACE("Closing file\n");
2774 IWineD3DSwapChain_Release(swapChain
);
2776 HeapFree(GetProcessHeap(), 0, allocatedMemory
);
2780 static HRESULT WINAPI
IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface
*iface
, WINED3DFORMAT format
) {
2781 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2784 TRACE("(%p) : Calling base function first\n", This
);
2785 hr
= IWineD3DBaseSurfaceImpl_SetFormat(iface
, format
);
2787 This
->Flags
&= ~(SFLAG_ALLOCATED
| SFLAG_SRGBALLOCATED
);
2788 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This
, This
->resource
.format_desc
->glFormat
,
2789 This
->resource
.format_desc
->glInternal
, This
->resource
.format_desc
->glType
);
2794 static HRESULT WINAPI
IWineD3DSurfaceImpl_SetMem(IWineD3DSurface
*iface
, void *Mem
) {
2795 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
2797 if(This
->Flags
& (SFLAG_LOCKED
| SFLAG_DCINUSE
)) {
2798 WARN("Surface is locked or the HDC is in use\n");
2799 return WINED3DERR_INVALIDCALL
;
2802 if(Mem
&& Mem
!= This
->resource
.allocatedMemory
) {
2803 void *release
= NULL
;
2805 /* Do I have to copy the old surface content? */
2806 if(This
->Flags
& SFLAG_DIBSECTION
) {
2807 /* Release the DC. No need to hold the critical section for the update
2808 * Thread because this thread runs only on front buffers, but this method
2809 * fails for render targets in the check above.
2811 SelectObject(This
->hDC
, This
->dib
.holdbitmap
);
2812 DeleteDC(This
->hDC
);
2813 /* Release the DIB section */
2814 DeleteObject(This
->dib
.DIBsection
);
2815 This
->dib
.bitmap_data
= NULL
;
2816 This
->resource
.allocatedMemory
= NULL
;
2818 This
->Flags
&= ~SFLAG_DIBSECTION
;
2819 } else if(!(This
->Flags
& SFLAG_USERPTR
)) {
2820 release
= This
->resource
.heapMemory
;
2821 This
->resource
.heapMemory
= NULL
;
2823 This
->resource
.allocatedMemory
= Mem
;
2824 This
->Flags
|= SFLAG_USERPTR
| SFLAG_INSYSMEM
;
2826 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2827 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INSYSMEM
, TRUE
);
2829 /* For client textures opengl has to be notified */
2830 if (This
->Flags
& SFLAG_CLIENT
)
2831 surface_release_client_storage(This
);
2833 /* Now free the old memory if any */
2834 HeapFree(GetProcessHeap(), 0, release
);
2835 } else if(This
->Flags
& SFLAG_USERPTR
) {
2836 /* LockRect and GetDC will re-create the dib section and allocated memory */
2837 This
->resource
.allocatedMemory
= NULL
;
2838 /* HeapMemory should be NULL already */
2839 if(This
->resource
.heapMemory
!= NULL
) ERR("User pointer surface has heap memory allocated\n");
2840 This
->Flags
&= ~SFLAG_USERPTR
;
2842 if (This
->Flags
& SFLAG_CLIENT
)
2843 surface_release_client_storage(This
);
2848 void flip_surface(IWineD3DSurfaceImpl
*front
, IWineD3DSurfaceImpl
*back
) {
2850 /* Flip the surface contents */
2855 front
->hDC
= back
->hDC
;
2859 /* Flip the DIBsection */
2862 BOOL hasDib
= front
->Flags
& SFLAG_DIBSECTION
;
2863 tmp
= front
->dib
.DIBsection
;
2864 front
->dib
.DIBsection
= back
->dib
.DIBsection
;
2865 back
->dib
.DIBsection
= tmp
;
2867 if(back
->Flags
& SFLAG_DIBSECTION
) front
->Flags
|= SFLAG_DIBSECTION
;
2868 else front
->Flags
&= ~SFLAG_DIBSECTION
;
2869 if(hasDib
) back
->Flags
|= SFLAG_DIBSECTION
;
2870 else back
->Flags
&= ~SFLAG_DIBSECTION
;
2873 /* Flip the surface data */
2877 tmp
= front
->dib
.bitmap_data
;
2878 front
->dib
.bitmap_data
= back
->dib
.bitmap_data
;
2879 back
->dib
.bitmap_data
= tmp
;
2881 tmp
= front
->resource
.allocatedMemory
;
2882 front
->resource
.allocatedMemory
= back
->resource
.allocatedMemory
;
2883 back
->resource
.allocatedMemory
= tmp
;
2885 tmp
= front
->resource
.heapMemory
;
2886 front
->resource
.heapMemory
= back
->resource
.heapMemory
;
2887 back
->resource
.heapMemory
= tmp
;
2892 GLuint tmp_pbo
= front
->pbo
;
2893 front
->pbo
= back
->pbo
;
2894 back
->pbo
= tmp_pbo
;
2897 /* client_memory should not be different, but just in case */
2900 tmp
= front
->dib
.client_memory
;
2901 front
->dib
.client_memory
= back
->dib
.client_memory
;
2902 back
->dib
.client_memory
= tmp
;
2905 /* Flip the opengl texture */
2909 tmp
= back
->texture_name
;
2910 back
->texture_name
= front
->texture_name
;
2911 front
->texture_name
= tmp
;
2913 tmp
= back
->texture_name_srgb
;
2914 back
->texture_name_srgb
= front
->texture_name_srgb
;
2915 front
->texture_name_srgb
= tmp
;
2919 DWORD tmp_flags
= back
->Flags
;
2920 back
->Flags
= front
->Flags
;
2921 front
->Flags
= tmp_flags
;
2925 static HRESULT WINAPI
IWineD3DSurfaceImpl_Flip(IWineD3DSurface
*iface
, IWineD3DSurface
*override
, DWORD Flags
) {
2926 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2927 IWineD3DSwapChainImpl
*swapchain
= NULL
;
2929 TRACE("(%p)->(%p,%x)\n", This
, override
, Flags
);
2931 /* Flipping is only supported on RenderTargets and overlays*/
2932 if( !(This
->resource
.usage
& (WINED3DUSAGE_RENDERTARGET
| WINED3DUSAGE_OVERLAY
)) ) {
2933 WARN("Tried to flip a non-render target, non-overlay surface\n");
2934 return WINEDDERR_NOTFLIPPABLE
;
2937 if(This
->resource
.usage
& WINED3DUSAGE_OVERLAY
) {
2938 flip_surface(This
, (IWineD3DSurfaceImpl
*) override
);
2940 /* Update the overlay if it is visible */
2941 if(This
->overlay_dest
) {
2942 return IWineD3DSurface_DrawOverlay((IWineD3DSurface
*) This
);
2949 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2950 * FIXME("(%p) Target override is not supported by now\n", This);
2951 * Additionally, it isn't really possible to support triple-buffering
2952 * properly on opengl at all
2956 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **) &swapchain
);
2958 ERR("Flipped surface is not on a swapchain\n");
2959 return WINEDDERR_NOTFLIPPABLE
;
2962 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2963 * and only d3d8 and d3d9 apps specify the presentation interval
2965 if((Flags
& (WINEDDFLIP_NOVSYNC
| WINEDDFLIP_INTERVAL2
| WINEDDFLIP_INTERVAL3
| WINEDDFLIP_INTERVAL4
)) == 0) {
2966 /* Most common case first to avoid wasting time on all the other cases */
2967 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_ONE
;
2968 } else if(Flags
& WINEDDFLIP_NOVSYNC
) {
2969 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_IMMEDIATE
;
2970 } else if(Flags
& WINEDDFLIP_INTERVAL2
) {
2971 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_TWO
;
2972 } else if(Flags
& WINEDDFLIP_INTERVAL3
) {
2973 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_THREE
;
2975 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_FOUR
;
2978 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2979 hr
= IWineD3DSwapChain_Present((IWineD3DSwapChain
*)swapchain
,
2980 NULL
, NULL
, swapchain
->win_handle
, NULL
, 0);
2981 IWineD3DSwapChain_Release((IWineD3DSwapChain
*) swapchain
);
2985 /* Does a direct frame buffer -> texture copy. Stretching is done
2986 * with single pixel copy calls
2988 static void fb_copy_to_texture_direct(IWineD3DSurfaceImpl
*dst_surface
, IWineD3DSurfaceImpl
*src_surface
,
2989 const RECT
*src_rect
, const RECT
*dst_rect_in
, WINED3DTEXTUREFILTERTYPE Filter
)
2991 IWineD3DDeviceImpl
*device
= dst_surface
->resource
.device
;
2994 struct wined3d_context
*context
;
2995 BOOL upsidedown
= FALSE
;
2996 RECT dst_rect
= *dst_rect_in
;
2998 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2999 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3001 if(dst_rect
.top
> dst_rect
.bottom
) {
3002 UINT tmp
= dst_rect
.bottom
;
3003 dst_rect
.bottom
= dst_rect
.top
;
3008 context
= context_acquire(device
, src_surface
, CTXUSAGE_BLIT
);
3009 surface_internal_preload(dst_surface
, SRGB_RGB
);
3012 /* Bind the target texture */
3013 glBindTexture(dst_surface
->texture_target
, dst_surface
->texture_name
);
3014 checkGLcall("glBindTexture");
3015 if (surface_is_offscreen(src_surface
))
3017 TRACE("Reading from an offscreen target\n");
3018 upsidedown
= !upsidedown
;
3019 glReadBuffer(device
->offscreenBuffer
);
3023 glReadBuffer(surface_get_gl_buffer(src_surface
));
3025 checkGLcall("glReadBuffer");
3027 xrel
= (float) (src_rect
->right
- src_rect
->left
) / (float) (dst_rect
.right
- dst_rect
.left
);
3028 yrel
= (float) (src_rect
->bottom
- src_rect
->top
) / (float) (dst_rect
.bottom
- dst_rect
.top
);
3030 if ((xrel
- 1.0f
< -eps
) || (xrel
- 1.0f
> eps
))
3032 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3034 if(Filter
!= WINED3DTEXF_NONE
&& Filter
!= WINED3DTEXF_POINT
) {
3035 ERR("Texture filtering not supported in direct blit\n");
3038 else if ((Filter
!= WINED3DTEXF_NONE
&& Filter
!= WINED3DTEXF_POINT
)
3039 && ((yrel
- 1.0f
< -eps
) || (yrel
- 1.0f
> eps
)))
3041 ERR("Texture filtering not supported in direct blit\n");
3045 && !((xrel
- 1.0f
< -eps
) || (xrel
- 1.0f
> eps
))
3046 && !((yrel
- 1.0f
< -eps
) || (yrel
- 1.0f
> eps
)))
3048 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3050 glCopyTexSubImage2D(dst_surface
->texture_target
, dst_surface
->texture_level
,
3051 dst_rect
.left
/*xoffset */, dst_rect
.top
/* y offset */,
3052 src_rect
->left
, src_surface
->currentDesc
.Height
- src_rect
->bottom
,
3053 dst_rect
.right
- dst_rect
.left
, dst_rect
.bottom
- dst_rect
.top
);
3055 UINT yoffset
= src_surface
->currentDesc
.Height
- src_rect
->top
+ dst_rect
.top
- 1;
3056 /* I have to process this row by row to swap the image,
3057 * otherwise it would be upside down, so stretching in y direction
3058 * doesn't cost extra time
3060 * However, stretching in x direction can be avoided if not necessary
3062 for(row
= dst_rect
.top
; row
< dst_rect
.bottom
; row
++) {
3063 if ((xrel
- 1.0f
< -eps
) || (xrel
- 1.0f
> eps
))
3065 /* Well, that stuff works, but it's very slow.
3066 * find a better way instead
3070 for (col
= dst_rect
.left
; col
< dst_rect
.right
; ++col
)
3072 glCopyTexSubImage2D(dst_surface
->texture_target
, dst_surface
->texture_level
,
3073 dst_rect
.left
+ col
/* x offset */, row
/* y offset */,
3074 src_rect
->left
+ col
* xrel
, yoffset
- (int) (row
* yrel
), 1, 1);
3079 glCopyTexSubImage2D(dst_surface
->texture_target
, dst_surface
->texture_level
,
3080 dst_rect
.left
/* x offset */, row
/* y offset */,
3081 src_rect
->left
, yoffset
- (int) (row
* yrel
), dst_rect
.right
- dst_rect
.left
, 1);
3085 checkGLcall("glCopyTexSubImage2D");
3088 context_release(context
);
3090 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3091 * path is never entered
3093 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)dst_surface
, SFLAG_INTEXTURE
, TRUE
);
3096 /* Uses the hardware to stretch and flip the image */
3097 static void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl
*dst_surface
, IWineD3DSurfaceImpl
*src_surface
,
3098 const RECT
*src_rect
, const RECT
*dst_rect_in
, WINED3DTEXTUREFILTERTYPE Filter
)
3100 IWineD3DDeviceImpl
*device
= dst_surface
->resource
.device
;
3101 GLuint src
, backup
= 0;
3102 IWineD3DSwapChainImpl
*src_swapchain
= NULL
;
3103 float left
, right
, top
, bottom
; /* Texture coordinates */
3104 UINT fbwidth
= src_surface
->currentDesc
.Width
;
3105 UINT fbheight
= src_surface
->currentDesc
.Height
;
3106 struct wined3d_context
*context
;
3107 GLenum drawBuffer
= GL_BACK
;
3108 GLenum texture_target
;
3109 BOOL noBackBufferBackup
;
3111 BOOL upsidedown
= FALSE
;
3112 RECT dst_rect
= *dst_rect_in
;
3114 TRACE("Using hwstretch blit\n");
3115 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3116 context
= context_acquire(device
, src_surface
, CTXUSAGE_BLIT
);
3117 surface_internal_preload(dst_surface
, SRGB_RGB
);
3119 src_offscreen
= surface_is_offscreen(src_surface
);
3120 noBackBufferBackup
= src_offscreen
&& wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
;
3121 if (!noBackBufferBackup
&& !src_surface
->texture_name
)
3123 /* Get it a description */
3124 surface_internal_preload(src_surface
, SRGB_RGB
);
3128 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3129 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3131 if (context
->aux_buffers
>= 2)
3133 /* Got more than one aux buffer? Use the 2nd aux buffer */
3134 drawBuffer
= GL_AUX1
;
3136 else if ((!src_offscreen
|| device
->offscreenBuffer
== GL_BACK
) && context
->aux_buffers
>= 1)
3138 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3139 drawBuffer
= GL_AUX0
;
3142 if(noBackBufferBackup
) {
3143 glGenTextures(1, &backup
);
3144 checkGLcall("glGenTextures");
3145 glBindTexture(GL_TEXTURE_2D
, backup
);
3146 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3147 texture_target
= GL_TEXTURE_2D
;
3149 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3150 * we are reading from the back buffer, the backup can be used as source texture
3152 texture_target
= src_surface
->texture_target
;
3153 glBindTexture(texture_target
, src_surface
->texture_name
);
3154 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
3155 glEnable(texture_target
);
3156 checkGLcall("glEnable(texture_target)");
3158 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3159 src_surface
->Flags
&= ~SFLAG_INTEXTURE
;
3162 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3163 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3165 if(dst_rect
.top
> dst_rect
.bottom
) {
3166 UINT tmp
= dst_rect
.bottom
;
3167 dst_rect
.bottom
= dst_rect
.top
;
3174 TRACE("Reading from an offscreen target\n");
3175 upsidedown
= !upsidedown
;
3176 glReadBuffer(device
->offscreenBuffer
);
3180 glReadBuffer(surface_get_gl_buffer(src_surface
));
3183 /* TODO: Only back up the part that will be overwritten */
3184 glCopyTexSubImage2D(texture_target
, 0,
3185 0, 0 /* read offsets */,
3190 checkGLcall("glCopyTexSubImage2D");
3192 /* No issue with overriding these - the sampler is dirty due to blit usage */
3193 glTexParameteri(texture_target
, GL_TEXTURE_MAG_FILTER
,
3194 wined3d_gl_mag_filter(magLookup
, Filter
));
3195 checkGLcall("glTexParameteri");
3196 glTexParameteri(texture_target
, GL_TEXTURE_MIN_FILTER
,
3197 wined3d_gl_min_mip_filter(minMipLookup
, Filter
, WINED3DTEXF_NONE
));
3198 checkGLcall("glTexParameteri");
3200 IWineD3DSurface_GetContainer((IWineD3DSurface
*)src_surface
, &IID_IWineD3DSwapChain
, (void **)&src_swapchain
);
3201 if (src_swapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*)src_swapchain
);
3202 if (!src_swapchain
|| src_surface
== src_swapchain
->back_buffers
[0])
3204 src
= backup
? backup
: src_surface
->texture_name
;
3208 glReadBuffer(GL_FRONT
);
3209 checkGLcall("glReadBuffer(GL_FRONT)");
3211 glGenTextures(1, &src
);
3212 checkGLcall("glGenTextures(1, &src)");
3213 glBindTexture(GL_TEXTURE_2D
, src
);
3214 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3216 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3217 * out for power of 2 sizes
3219 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, src_surface
->pow2Width
,
3220 src_surface
->pow2Height
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
3221 checkGLcall("glTexImage2D");
3222 glCopyTexSubImage2D(GL_TEXTURE_2D
, 0,
3223 0, 0 /* read offsets */,
3228 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
3229 checkGLcall("glTexParameteri");
3230 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
3231 checkGLcall("glTexParameteri");
3233 glReadBuffer(GL_BACK
);
3234 checkGLcall("glReadBuffer(GL_BACK)");
3236 if(texture_target
!= GL_TEXTURE_2D
) {
3237 glDisable(texture_target
);
3238 glEnable(GL_TEXTURE_2D
);
3239 texture_target
= GL_TEXTURE_2D
;
3242 checkGLcall("glEnd and previous");
3244 left
= src_rect
->left
;
3245 right
= src_rect
->right
;
3249 top
= src_surface
->currentDesc
.Height
- src_rect
->top
;
3250 bottom
= src_surface
->currentDesc
.Height
- src_rect
->bottom
;
3254 top
= src_surface
->currentDesc
.Height
- src_rect
->bottom
;
3255 bottom
= src_surface
->currentDesc
.Height
- src_rect
->top
;
3258 if (src_surface
->Flags
& SFLAG_NORMCOORD
)
3260 left
/= src_surface
->pow2Width
;
3261 right
/= src_surface
->pow2Width
;
3262 top
/= src_surface
->pow2Height
;
3263 bottom
/= src_surface
->pow2Height
;
3266 /* draw the source texture stretched and upside down. The correct surface is bound already */
3267 glTexParameteri(texture_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
3268 glTexParameteri(texture_target
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
3270 context_set_draw_buffer(context
, drawBuffer
);
3271 glReadBuffer(drawBuffer
);
3275 glTexCoord2f(left
, bottom
);
3276 glVertex2i(0, fbheight
);
3279 glTexCoord2f(left
, top
);
3280 glVertex2i(0, fbheight
- dst_rect
.bottom
- dst_rect
.top
);
3283 glTexCoord2f(right
, top
);
3284 glVertex2i(dst_rect
.right
- dst_rect
.left
, fbheight
- dst_rect
.bottom
- dst_rect
.top
);
3287 glTexCoord2f(right
, bottom
);
3288 glVertex2i(dst_rect
.right
- dst_rect
.left
, fbheight
);
3290 checkGLcall("glEnd and previous");
3292 if (texture_target
!= dst_surface
->texture_target
)
3294 glDisable(texture_target
);
3295 glEnable(dst_surface
->texture_target
);
3296 texture_target
= dst_surface
->texture_target
;
3299 /* Now read the stretched and upside down image into the destination texture */
3300 glBindTexture(texture_target
, dst_surface
->texture_name
);
3301 checkGLcall("glBindTexture");
3302 glCopyTexSubImage2D(texture_target
,
3304 dst_rect
.left
, dst_rect
.top
, /* xoffset, yoffset */
3305 0, 0, /* We blitted the image to the origin */
3306 dst_rect
.right
- dst_rect
.left
, dst_rect
.bottom
- dst_rect
.top
);
3307 checkGLcall("glCopyTexSubImage2D");
3309 if(drawBuffer
== GL_BACK
) {
3310 /* Write the back buffer backup back */
3312 if(texture_target
!= GL_TEXTURE_2D
) {
3313 glDisable(texture_target
);
3314 glEnable(GL_TEXTURE_2D
);
3315 texture_target
= GL_TEXTURE_2D
;
3317 glBindTexture(GL_TEXTURE_2D
, backup
);
3318 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3322 if (texture_target
!= src_surface
->texture_target
)
3324 glDisable(texture_target
);
3325 glEnable(src_surface
->texture_target
);
3326 texture_target
= src_surface
->texture_target
;
3328 glBindTexture(src_surface
->texture_target
, src_surface
->texture_name
);
3329 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
3334 glTexCoord2f(0.0f
, (float)fbheight
/ (float)src_surface
->pow2Height
);
3338 glTexCoord2f(0.0f
, 0.0f
);
3339 glVertex2i(0, fbheight
);
3342 glTexCoord2f((float)fbwidth
/ (float)src_surface
->pow2Width
, 0.0f
);
3343 glVertex2i(fbwidth
, src_surface
->currentDesc
.Height
);
3346 glTexCoord2f((float)fbwidth
/ (float)src_surface
->pow2Width
,
3347 (float)fbheight
/ (float)src_surface
->pow2Height
);
3348 glVertex2i(fbwidth
, 0);
3351 glDisable(texture_target
);
3352 checkGLcall("glDisable(texture_target)");
3355 if (src
!= src_surface
->texture_name
&& src
!= backup
)
3357 glDeleteTextures(1, &src
);
3358 checkGLcall("glDeleteTextures(1, &src)");
3361 glDeleteTextures(1, &backup
);
3362 checkGLcall("glDeleteTextures(1, &backup)");
3367 if (wined3d_settings
.strict_draw_ordering
) wglFlush(); /* Flush to ensure ordering across contexts. */
3369 context_release(context
);
3371 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3372 * path is never entered
3374 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)dst_surface
, SFLAG_INTEXTURE
, TRUE
);
3377 /* Until the blit_shader is ready, define some prototypes here. */
3378 static BOOL
fbo_blit_supported(const struct wined3d_gl_info
*gl_info
, enum blit_operation blit_op
,
3379 const RECT
*src_rect
, DWORD src_usage
, WINED3DPOOL src_pool
,
3380 const struct wined3d_format_desc
*src_format_desc
,
3381 const RECT
*dst_rect
, DWORD dst_usage
, WINED3DPOOL dst_pool
,
3382 const struct wined3d_format_desc
*dst_format_desc
);
3384 /* Not called from the VTable */
3385 static HRESULT
IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl
*dst_surface
, const RECT
*DestRect
,
3386 IWineD3DSurfaceImpl
*src_surface
, const RECT
*SrcRect
, DWORD Flags
, const WINEDDBLTFX
*DDBltFx
,
3387 WINED3DTEXTUREFILTERTYPE Filter
)
3389 IWineD3DDeviceImpl
*device
= dst_surface
->resource
.device
;
3390 IWineD3DSwapChainImpl
*srcSwapchain
= NULL
, *dstSwapchain
= NULL
;
3391 RECT dst_rect
, src_rect
;
3393 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3394 dst_surface
, wine_dbgstr_rect(DestRect
), src_surface
, wine_dbgstr_rect(SrcRect
),
3395 Flags
, DDBltFx
, debug_d3dtexturefiltertype(Filter
));
3397 /* Get the swapchain. One of the surfaces has to be a primary surface */
3398 if (dst_surface
->resource
.pool
== WINED3DPOOL_SYSTEMMEM
)
3400 WARN("Destination is in sysmem, rejecting gl blt\n");
3401 return WINED3DERR_INVALIDCALL
;
3403 IWineD3DSurface_GetContainer((IWineD3DSurface
*)dst_surface
, &IID_IWineD3DSwapChain
, (void **)&dstSwapchain
);
3404 if (dstSwapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*)dstSwapchain
);
3407 if (src_surface
->resource
.pool
== WINED3DPOOL_SYSTEMMEM
)
3409 WARN("Src is in sysmem, rejecting gl blt\n");
3410 return WINED3DERR_INVALIDCALL
;
3412 IWineD3DSurface_GetContainer((IWineD3DSurface
*)src_surface
, &IID_IWineD3DSwapChain
, (void **)&srcSwapchain
);
3413 if (srcSwapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*)srcSwapchain
);
3416 /* Early sort out of cases where no render target is used */
3417 if (!dstSwapchain
&& !srcSwapchain
3418 && src_surface
!= device
->render_targets
[0]
3419 && dst_surface
!= device
->render_targets
[0])
3421 TRACE("No surface is render target, not using hardware blit.\n");
3422 return WINED3DERR_INVALIDCALL
;
3425 /* No destination color keying supported */
3426 if(Flags
& (WINEDDBLT_KEYDEST
| WINEDDBLT_KEYDESTOVERRIDE
)) {
3427 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3428 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3429 return WINED3DERR_INVALIDCALL
;
3432 surface_get_rect(dst_surface
, DestRect
, &dst_rect
);
3433 if (src_surface
) surface_get_rect(src_surface
, SrcRect
, &src_rect
);
3435 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3436 if (dstSwapchain
&& dstSwapchain
== srcSwapchain
&& dstSwapchain
->back_buffers
3437 && dst_surface
== dstSwapchain
->front_buffer
3438 && src_surface
== dstSwapchain
->back_buffers
[0])
3440 /* Half-life does a Blt from the back buffer to the front buffer,
3441 * Full surface size, no flags... Use present instead
3443 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3446 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3449 TRACE("Looking if a Present can be done...\n");
3450 /* Source Rectangle must be full surface */
3451 if (src_rect
.left
|| src_rect
.top
3452 || src_rect
.right
!= src_surface
->currentDesc
.Width
3453 || src_rect
.bottom
!= src_surface
->currentDesc
.Height
)
3455 TRACE("No, Source rectangle doesn't match\n");
3459 /* No stretching may occur */
3460 if(src_rect
.right
!= dst_rect
.right
- dst_rect
.left
||
3461 src_rect
.bottom
!= dst_rect
.bottom
- dst_rect
.top
) {
3462 TRACE("No, stretching is done\n");
3466 /* Destination must be full surface or match the clipping rectangle */
3467 if (dst_surface
->clipper
&& ((IWineD3DClipperImpl
*)dst_surface
->clipper
)->hWnd
)
3471 GetClientRect(((IWineD3DClipperImpl
*)dst_surface
->clipper
)->hWnd
, &cliprect
);
3472 pos
[0].x
= dst_rect
.left
;
3473 pos
[0].y
= dst_rect
.top
;
3474 pos
[1].x
= dst_rect
.right
;
3475 pos
[1].y
= dst_rect
.bottom
;
3476 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl
*)dst_surface
->clipper
)->hWnd
, pos
, 2);
3478 if(pos
[0].x
!= cliprect
.left
|| pos
[0].y
!= cliprect
.top
||
3479 pos
[1].x
!= cliprect
.right
|| pos
[1].y
!= cliprect
.bottom
)
3481 TRACE("No, dest rectangle doesn't match(clipper)\n");
3482 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect
));
3483 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect
));
3487 else if (dst_rect
.left
|| dst_rect
.top
3488 || dst_rect
.right
!= dst_surface
->currentDesc
.Width
3489 || dst_rect
.bottom
!= dst_surface
->currentDesc
.Height
)
3491 TRACE("No, dest rectangle doesn't match(surface size)\n");
3497 /* These flags are unimportant for the flag check, remove them */
3498 if((Flags
& ~(WINEDDBLT_DONOTWAIT
| WINEDDBLT_WAIT
)) == 0) {
3499 WINED3DSWAPEFFECT orig_swap
= dstSwapchain
->presentParms
.SwapEffect
;
3501 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3502 * take very long, while a flip is fast.
3503 * This applies to Half-Life, which does such Blts every time it finished
3504 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3505 * menu. This is also used by all apps when they do windowed rendering
3507 * The problem is that flipping is not really the same as copying. After a
3508 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3509 * untouched. Therefore it's necessary to override the swap effect
3510 * and to set it back after the flip.
3512 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3516 dstSwapchain
->presentParms
.SwapEffect
= WINED3DSWAPEFFECT_COPY
;
3517 dstSwapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_IMMEDIATE
;
3519 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3520 IWineD3DSwapChain_Present((IWineD3DSwapChain
*)dstSwapchain
,
3521 NULL
, NULL
, dstSwapchain
->win_handle
, NULL
, 0);
3523 dstSwapchain
->presentParms
.SwapEffect
= orig_swap
;
3530 TRACE("Unsupported blit between buffers on the same swapchain\n");
3531 return WINED3DERR_INVALIDCALL
;
3532 } else if(dstSwapchain
&& dstSwapchain
== srcSwapchain
) {
3533 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3534 return WINED3DERR_INVALIDCALL
;
3535 } else if(dstSwapchain
&& srcSwapchain
) {
3536 FIXME("Implement hardware blit between two different swapchains\n");
3537 return WINED3DERR_INVALIDCALL
;
3539 else if (dstSwapchain
)
3541 /* Handled with regular texture -> swapchain blit */
3542 if (src_surface
== device
->render_targets
[0])
3543 TRACE("Blit from active render target to a swapchain\n");
3545 else if (srcSwapchain
&& dst_surface
== device
->render_targets
[0])
3547 FIXME("Implement blit from a swapchain to the active render target\n");
3548 return WINED3DERR_INVALIDCALL
;
3551 if ((srcSwapchain
|| src_surface
== device
->render_targets
[0]) && !dstSwapchain
)
3553 /* Blit from render target to texture */
3556 /* P8 read back is not implemented */
3557 if (src_surface
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
3558 || dst_surface
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
)
3560 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3561 return WINED3DERR_INVALIDCALL
;
3564 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
3565 TRACE("Color keying not supported by frame buffer to texture blit\n");
3566 return WINED3DERR_INVALIDCALL
;
3567 /* Destination color key is checked above */
3570 if(dst_rect
.right
- dst_rect
.left
!= src_rect
.right
- src_rect
.left
) {
3576 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3577 * flip the image nor scale it.
3579 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3580 * -> If the app wants a image width an unscaled width, copy it line per line
3581 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3582 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3583 * back buffer. This is slower than reading line per line, thus not used for flipping
3584 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3587 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3588 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3591 if (fbo_blit_supported(&device
->adapter
->gl_info
, BLIT_OP_BLIT
,
3592 &src_rect
, src_surface
->resource
.usage
, src_surface
->resource
.pool
, src_surface
->resource
.format_desc
,
3593 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
, dst_surface
->resource
.format_desc
))
3595 stretch_rect_fbo(device
, src_surface
, &src_rect
, dst_surface
, &dst_rect
, Filter
);
3597 else if (!stretchx
|| dst_rect
.right
- dst_rect
.left
> src_surface
->currentDesc
.Width
3598 || dst_rect
.bottom
- dst_rect
.top
> src_surface
->currentDesc
.Height
)
3600 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3601 fb_copy_to_texture_direct(dst_surface
, src_surface
, &src_rect
, &dst_rect
, Filter
);
3603 TRACE("Using hardware stretching to flip / stretch the texture\n");
3604 fb_copy_to_texture_hwstretch(dst_surface
, src_surface
, &src_rect
, &dst_rect
, Filter
);
3607 if (!(dst_surface
->Flags
& SFLAG_DONOTFREE
))
3609 HeapFree(GetProcessHeap(), 0, dst_surface
->resource
.heapMemory
);
3610 dst_surface
->resource
.allocatedMemory
= NULL
;
3611 dst_surface
->resource
.heapMemory
= NULL
;
3615 dst_surface
->Flags
&= ~SFLAG_INSYSMEM
;
3620 else if (src_surface
)
3622 /* Blit from offscreen surface to render target */
3623 DWORD oldCKeyFlags
= src_surface
->CKeyFlags
;
3624 WINEDDCOLORKEY oldBltCKey
= src_surface
->SrcBltCKey
;
3625 struct wined3d_context
*context
;
3627 TRACE("Blt from surface %p to rendertarget %p\n", src_surface
, dst_surface
);
3629 if (!(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
))
3630 && fbo_blit_supported(&device
->adapter
->gl_info
, BLIT_OP_BLIT
,
3631 &src_rect
, src_surface
->resource
.usage
, src_surface
->resource
.pool
,
3632 src_surface
->resource
.format_desc
,
3633 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
,
3634 dst_surface
->resource
.format_desc
))
3636 TRACE("Using stretch_rect_fbo\n");
3637 /* The source is always a texture, but never the currently active render target, and the texture
3638 * contents are never upside down. */
3639 stretch_rect_fbo(device
, src_surface
, &src_rect
, dst_surface
, &dst_rect
, Filter
);
3643 if (!(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
))
3644 && arbfp_blit
.blit_supported(&device
->adapter
->gl_info
, BLIT_OP_BLIT
,
3645 &src_rect
, src_surface
->resource
.usage
, src_surface
->resource
.pool
,
3646 src_surface
->resource
.format_desc
,
3647 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
,
3648 dst_surface
->resource
.format_desc
))
3650 return arbfp_blit_surface(device
, src_surface
, &src_rect
, dst_surface
, &dst_rect
, BLIT_OP_BLIT
, Filter
);
3653 /* Color keying: Check if we have to do a color keyed blt,
3654 * and if not check if a color key is activated.
3656 * Just modify the color keying parameters in the surface and restore them afterwards
3657 * The surface keeps track of the color key last used to load the opengl surface.
3658 * PreLoad will catch the change to the flags and color key and reload if necessary.
3660 if(Flags
& WINEDDBLT_KEYSRC
) {
3661 /* Use color key from surface */
3662 } else if(Flags
& WINEDDBLT_KEYSRCOVERRIDE
) {
3663 /* Use color key from DDBltFx */
3664 src_surface
->CKeyFlags
|= WINEDDSD_CKSRCBLT
;
3665 src_surface
->SrcBltCKey
= DDBltFx
->ddckSrcColorkey
;
3667 /* Do not use color key */
3668 src_surface
->CKeyFlags
&= ~WINEDDSD_CKSRCBLT
;
3671 /* Now load the surface */
3672 surface_internal_preload(src_surface
, SRGB_RGB
);
3674 /* Activate the destination context, set it up for blitting */
3675 context
= context_acquire(device
, dst_surface
, CTXUSAGE_BLIT
);
3677 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3678 * while OpenGL coordinates are window relative.
3679 * Also beware of the origin difference(top left vs bottom left).
3680 * Also beware that the front buffer's surface size is screen width x screen height,
3681 * whereas the real gl drawable size is the size of the window.
3683 if (dstSwapchain
&& dst_surface
== dstSwapchain
->front_buffer
)
3686 POINT offset
= {0,0};
3688 ClientToScreen(context
->win_handle
, &offset
);
3689 GetClientRect(context
->win_handle
, &windowsize
);
3690 h
= windowsize
.bottom
- windowsize
.top
;
3691 dst_rect
.left
-= offset
.x
; dst_rect
.right
-=offset
.x
;
3692 dst_rect
.top
-= offset
.y
; dst_rect
.bottom
-=offset
.y
;
3693 dst_rect
.top
+= dst_surface
->currentDesc
.Height
- h
;
3694 dst_rect
.bottom
+= dst_surface
->currentDesc
.Height
- h
;
3697 if (!device
->blitter
->blit_supported(&device
->adapter
->gl_info
, BLIT_OP_BLIT
,
3698 &src_rect
, src_surface
->resource
.usage
, src_surface
->resource
.pool
, src_surface
->resource
.format_desc
,
3699 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
, dst_surface
->resource
.format_desc
))
3701 FIXME("Unsupported blit operation falling back to software\n");
3702 return WINED3DERR_INVALIDCALL
;
3705 device
->blitter
->set_shader((IWineD3DDevice
*)device
, src_surface
);
3709 /* This is for color keying */
3710 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
3711 glEnable(GL_ALPHA_TEST
);
3712 checkGLcall("glEnable(GL_ALPHA_TEST)");
3714 /* When the primary render target uses P8, the alpha component contains the palette index.
3715 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3716 * should be masked away have alpha set to 0. */
3717 if (primary_render_target_is_p8(device
))
3718 glAlphaFunc(GL_NOTEQUAL
, (float)src_surface
->SrcBltCKey
.dwColorSpaceLowValue
/ 256.0f
);
3720 glAlphaFunc(GL_NOTEQUAL
, 0.0f
);
3721 checkGLcall("glAlphaFunc");
3723 glDisable(GL_ALPHA_TEST
);
3724 checkGLcall("glDisable(GL_ALPHA_TEST)");
3727 /* Draw a textured quad
3729 draw_textured_quad(src_surface
, &src_rect
, &dst_rect
, Filter
);
3731 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
3732 glDisable(GL_ALPHA_TEST
);
3733 checkGLcall("glDisable(GL_ALPHA_TEST)");
3736 /* Restore the color key parameters */
3737 src_surface
->CKeyFlags
= oldCKeyFlags
;
3738 src_surface
->SrcBltCKey
= oldBltCKey
;
3742 /* Leave the opengl state valid for blitting */
3743 device
->blitter
->unset_shader((IWineD3DDevice
*)device
);
3745 if (wined3d_settings
.strict_draw_ordering
|| (dstSwapchain
3746 && (dst_surface
== dstSwapchain
->front_buffer
3747 || dstSwapchain
->num_contexts
> 1)))
3748 wglFlush(); /* Flush to ensure ordering across contexts. */
3750 context_release(context
);
3752 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3753 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3756 IWineD3DSurface_ModifyLocation((IWineD3DSurface
*)dst_surface
, SFLAG_INDRAWABLE
, TRUE
);
3760 /* Source-Less Blit to render target */
3761 if (Flags
& WINEDDBLT_COLORFILL
) {
3764 TRACE("Colorfill\n");
3766 /* The color as given in the Blt function is in the format of the frame-buffer...
3767 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3769 if (!surface_convert_color_to_argb(dst_surface
, DDBltFx
->u5
.dwFillColor
, &color
))
3771 /* The color conversion function already prints an error, so need to do it here */
3772 return WINED3DERR_INVALIDCALL
;
3775 if (ffp_blit
.blit_supported(&device
->adapter
->gl_info
, BLIT_OP_COLOR_FILL
,
3777 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
,
3778 dst_surface
->resource
.format_desc
))
3780 return ffp_blit
.color_fill(device
, dst_surface
, &dst_rect
, color
);
3782 else if (cpu_blit
.blit_supported(&device
->adapter
->gl_info
, BLIT_OP_COLOR_FILL
,
3784 &dst_rect
, dst_surface
->resource
.usage
, dst_surface
->resource
.pool
,
3785 dst_surface
->resource
.format_desc
))
3787 return cpu_blit
.color_fill(device
, dst_surface
, &dst_rect
, color
);
3789 return WINED3DERR_INVALIDCALL
;
3793 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3794 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3795 return WINED3DERR_INVALIDCALL
;
3798 static HRESULT
IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl
*This
, const RECT
*DestRect
,
3799 IWineD3DSurface
*SrcSurface
, const RECT
*SrcRect
, DWORD Flags
, const WINEDDBLTFX
*DDBltFx
)
3801 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
3804 if (Flags
& WINEDDBLT_DEPTHFILL
) {
3805 switch(This
->resource
.format_desc
->format
)
3807 case WINED3DFMT_D16_UNORM
:
3808 depth
= (float) DDBltFx
->u5
.dwFillDepth
/ (float) 0x0000ffff;
3810 case WINED3DFMT_S1_UINT_D15_UNORM
:
3811 depth
= (float) DDBltFx
->u5
.dwFillDepth
/ (float) 0x0000fffe;
3813 case WINED3DFMT_D24_UNORM_S8_UINT
:
3814 case WINED3DFMT_X8D24_UNORM
:
3815 depth
= (float) DDBltFx
->u5
.dwFillDepth
/ (float) 0x00ffffff;
3817 case WINED3DFMT_D32_UNORM
:
3818 depth
= (float) DDBltFx
->u5
.dwFillDepth
/ (float) 0xffffffff;
3822 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This
->resource
.format_desc
->format
));
3825 return IWineD3DDevice_Clear((IWineD3DDevice
*)device
, DestRect
? 1 : 0, (const WINED3DRECT
*)DestRect
,
3826 WINED3DCLEAR_ZBUFFER
, 0x00000000, depth
, 0x00000000);
3829 FIXME("(%p): Unsupp depthstencil blit\n", This
);
3830 return WINED3DERR_INVALIDCALL
;
3833 static HRESULT WINAPI
IWineD3DSurfaceImpl_Blt(IWineD3DSurface
*iface
, const RECT
*DestRect
, IWineD3DSurface
*SrcSurface
,
3834 const RECT
*SrcRect
, DWORD Flags
, const WINEDDBLTFX
*DDBltFx
, WINED3DTEXTUREFILTERTYPE Filter
) {
3835 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
3836 IWineD3DSurfaceImpl
*Src
= (IWineD3DSurfaceImpl
*) SrcSurface
;
3837 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
3839 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
);
3840 TRACE("(%p): Usage is %s\n", This
, debug_d3dusage(This
->resource
.usage
));
3842 if ( (This
->Flags
& SFLAG_LOCKED
) || ((Src
!= NULL
) && (Src
->Flags
& SFLAG_LOCKED
)))
3844 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3845 return WINEDDERR_SURFACEBUSY
;
3848 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3849 * except depth blits, which seem to work
3851 if (This
== device
->depth_stencil
|| (Src
&& Src
== device
->depth_stencil
))
3853 if (device
->inScene
&& !(Flags
& WINEDDBLT_DEPTHFILL
))
3855 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3856 return WINED3DERR_INVALIDCALL
;
3857 } else if(IWineD3DSurfaceImpl_BltZ(This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
) == WINED3D_OK
) {
3858 TRACE("Z Blit override handled the blit\n");
3863 /* Special cases for RenderTargets */
3864 if ((This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)
3865 || (Src
&& (Src
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)))
3867 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This
, DestRect
, Src
, SrcRect
, Flags
, DDBltFx
, Filter
)))
3871 /* For the rest call the X11 surface implementation.
3872 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3873 * other Blts are rather rare
3875 return IWineD3DBaseSurfaceImpl_Blt(iface
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
, Filter
);
3878 static HRESULT WINAPI
IWineD3DSurfaceImpl_BltFast(IWineD3DSurface
*iface
, DWORD dstx
, DWORD dsty
,
3879 IWineD3DSurface
*Source
, const RECT
*rsrc
, DWORD trans
)
3881 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3882 IWineD3DSurfaceImpl
*srcImpl
= (IWineD3DSurfaceImpl
*) Source
;
3883 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
3885 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface
, dstx
, dsty
, Source
, rsrc
, trans
);
3887 if ( (This
->Flags
& SFLAG_LOCKED
) || (srcImpl
->Flags
& SFLAG_LOCKED
))
3889 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3890 return WINEDDERR_SURFACEBUSY
;
3893 if (device
->inScene
&& (This
== device
->depth_stencil
|| srcImpl
== device
->depth_stencil
))
3895 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3896 return WINED3DERR_INVALIDCALL
;
3899 /* Special cases for RenderTargets */
3900 if( (This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) ||
3901 (srcImpl
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) ) {
3903 RECT SrcRect
, DstRect
;
3906 surface_get_rect(srcImpl
, rsrc
, &SrcRect
);
3908 DstRect
.left
= dstx
;
3910 DstRect
.right
= dstx
+ SrcRect
.right
- SrcRect
.left
;
3911 DstRect
.bottom
= dsty
+ SrcRect
.bottom
- SrcRect
.top
;
3913 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3914 if(trans
& WINEDDBLTFAST_SRCCOLORKEY
)
3915 Flags
|= WINEDDBLT_KEYSRC
;
3916 if(trans
& WINEDDBLTFAST_DESTCOLORKEY
)
3917 Flags
|= WINEDDBLT_KEYDEST
;
3918 if(trans
& WINEDDBLTFAST_WAIT
)
3919 Flags
|= WINEDDBLT_WAIT
;
3920 if(trans
& WINEDDBLTFAST_DONOTWAIT
)
3921 Flags
|= WINEDDBLT_DONOTWAIT
;
3923 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This
,
3924 &DstRect
, srcImpl
, &SrcRect
, Flags
, NULL
, WINED3DTEXF_POINT
)))
3929 return IWineD3DBaseSurfaceImpl_BltFast(iface
, dstx
, dsty
, Source
, rsrc
, trans
);
3932 static HRESULT WINAPI
IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface
*iface
)
3934 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3936 IWineD3DPaletteImpl
*pal
= This
->palette
;
3938 TRACE("(%p)\n", This
);
3940 if (!pal
) return WINED3D_OK
;
3942 if (This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
3943 || This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT_A8_UNORM
)
3945 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)
3947 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3948 IWineD3DSurface_LoadLocation(iface
, SFLAG_INTEXTURE
, NULL
);
3950 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3951 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INDRAWABLE
, FALSE
);
3953 if(!(This
->Flags
& SFLAG_INSYSMEM
)) {
3954 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3955 IWineD3DSurface_LoadLocation(iface
, SFLAG_INSYSMEM
, NULL
);
3957 TRACE("Dirtifying surface\n");
3958 IWineD3DSurface_ModifyLocation(iface
, SFLAG_INSYSMEM
, TRUE
);
3962 if(This
->Flags
& SFLAG_DIBSECTION
) {
3963 TRACE("(%p): Updating the hdc's palette\n", This
);
3964 for (n
=0; n
<256; n
++) {
3965 col
[n
].rgbRed
= pal
->palents
[n
].peRed
;
3966 col
[n
].rgbGreen
= pal
->palents
[n
].peGreen
;
3967 col
[n
].rgbBlue
= pal
->palents
[n
].peBlue
;
3968 col
[n
].rgbReserved
= 0;
3970 SetDIBColorTable(This
->hDC
, 0, 256, col
);
3973 /* Propagate the changes to the drawable when we have a palette. */
3974 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)
3975 IWineD3DSurface_LoadLocation(iface
, SFLAG_INDRAWABLE
, NULL
);
3980 static HRESULT WINAPI
IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface
*iface
) {
3981 /** Check against the maximum texture sizes supported by the video card **/
3982 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3983 const struct wined3d_gl_info
*gl_info
= &This
->resource
.device
->adapter
->gl_info
;
3984 unsigned int pow2Width
, pow2Height
;
3986 This
->texture_name
= 0;
3987 This
->texture_target
= GL_TEXTURE_2D
;
3989 /* Non-power2 support */
3990 if (gl_info
->supported
[ARB_TEXTURE_NON_POWER_OF_TWO
] || gl_info
->supported
[WINE_NORMALIZED_TEXRECT
])
3992 pow2Width
= This
->currentDesc
.Width
;
3993 pow2Height
= This
->currentDesc
.Height
;
3997 /* Find the nearest pow2 match */
3998 pow2Width
= pow2Height
= 1;
3999 while (pow2Width
< This
->currentDesc
.Width
) pow2Width
<<= 1;
4000 while (pow2Height
< This
->currentDesc
.Height
) pow2Height
<<= 1;
4002 This
->pow2Width
= pow2Width
;
4003 This
->pow2Height
= pow2Height
;
4005 if (pow2Width
> This
->currentDesc
.Width
|| pow2Height
> This
->currentDesc
.Height
) {
4006 /** TODO: add support for non power two compressed textures **/
4007 if (This
->resource
.format_desc
->Flags
& WINED3DFMT_FLAG_COMPRESSED
)
4009 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4010 This
, This
->currentDesc
.Width
, This
->currentDesc
.Height
);
4011 return WINED3DERR_NOTAVAILABLE
;
4015 if(pow2Width
!= This
->currentDesc
.Width
||
4016 pow2Height
!= This
->currentDesc
.Height
) {
4017 This
->Flags
|= SFLAG_NONPOW2
;
4020 TRACE("%p\n", This
);
4021 if ((This
->pow2Width
> gl_info
->limits
.texture_size
|| This
->pow2Height
> gl_info
->limits
.texture_size
)
4022 && !(This
->resource
.usage
& (WINED3DUSAGE_RENDERTARGET
| WINED3DUSAGE_DEPTHSTENCIL
)))
4024 /* one of three options
4025 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)
4026 2: Set the texture to the maximum size (bad idea)
4027 3: WARN and return WINED3DERR_NOTAVAILABLE;
4028 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.
4030 if(This
->resource
.pool
== WINED3DPOOL_DEFAULT
|| This
->resource
.pool
== WINED3DPOOL_MANAGED
)
4032 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This
);
4033 return WINED3DERR_NOTAVAILABLE
;
4036 /* We should never use this surface in combination with OpenGL! */
4037 TRACE("(%p) Creating an oversized surface: %ux%u\n", This
, This
->pow2Width
, This
->pow2Height
);
4041 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4042 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4043 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4045 if (This
->Flags
& SFLAG_NONPOW2
&& gl_info
->supported
[ARB_TEXTURE_RECTANGLE
]
4046 && !(This
->resource
.format_desc
->format
== WINED3DFMT_P8_UINT
4047 && gl_info
->supported
[EXT_PALETTED_TEXTURE
]
4048 && wined3d_settings
.rendertargetlock_mode
== RTL_READTEX
))
4050 This
->texture_target
= GL_TEXTURE_RECTANGLE_ARB
;
4051 This
->pow2Width
= This
->currentDesc
.Width
;
4052 This
->pow2Height
= This
->currentDesc
.Height
;
4053 This
->Flags
&= ~(SFLAG_NONPOW2
| SFLAG_NORMCOORD
);
4057 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) {
4058 switch(wined3d_settings
.offscreen_rendering_mode
) {
4059 case ORM_FBO
: This
->get_drawable_size
= get_drawable_size_fbo
; break;
4060 case ORM_BACKBUFFER
: This
->get_drawable_size
= get_drawable_size_backbuffer
; break;
4064 This
->Flags
|= SFLAG_INSYSMEM
;
4069 /* GL locking is done by the caller */
4070 static void surface_depth_blt(IWineD3DSurfaceImpl
*This
, const struct wined3d_gl_info
*gl_info
,
4071 GLuint texture
, GLsizei w
, GLsizei h
, GLenum target
)
4073 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
4074 struct blt_info info
;
4075 GLint old_binding
= 0;
4077 glPushAttrib(GL_ENABLE_BIT
| GL_DEPTH_BUFFER_BIT
| GL_COLOR_BUFFER_BIT
| GL_VIEWPORT_BIT
);
4079 glDisable(GL_CULL_FACE
);
4080 glDisable(GL_BLEND
);
4081 glDisable(GL_ALPHA_TEST
);
4082 glDisable(GL_SCISSOR_TEST
);
4083 glDisable(GL_STENCIL_TEST
);
4084 glEnable(GL_DEPTH_TEST
);
4085 glDepthFunc(GL_ALWAYS
);
4086 glDepthMask(GL_TRUE
);
4087 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
4088 glViewport(0, 0, w
, h
);
4090 surface_get_blt_info(target
, NULL
, w
, h
, &info
);
4091 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
));
4092 glGetIntegerv(info
.binding
, &old_binding
);
4093 glBindTexture(info
.bind_target
, texture
);
4095 device
->shader_backend
->shader_select_depth_blt((IWineD3DDevice
*)device
,
4096 info
.tex_type
, &This
->ds_current_size
);
4098 glBegin(GL_TRIANGLE_STRIP
);
4099 glTexCoord3fv(info
.coords
[0]);
4100 glVertex2f(-1.0f
, -1.0f
);
4101 glTexCoord3fv(info
.coords
[1]);
4102 glVertex2f(1.0f
, -1.0f
);
4103 glTexCoord3fv(info
.coords
[2]);
4104 glVertex2f(-1.0f
, 1.0f
);
4105 glTexCoord3fv(info
.coords
[3]);
4106 glVertex2f(1.0f
, 1.0f
);
4109 glBindTexture(info
.bind_target
, old_binding
);
4113 device
->shader_backend
->shader_deselect_depth_blt((IWineD3DDevice
*)device
);
4116 void surface_modify_ds_location(IWineD3DSurfaceImpl
*surface
,
4117 DWORD location
, UINT w
, UINT h
)
4119 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface
, location
, w
, h
);
4121 if (location
& ~SFLAG_DS_LOCATIONS
)
4122 FIXME("Invalid location (%#x) specified.\n", location
);
4124 surface
->ds_current_size
.cx
= w
;
4125 surface
->ds_current_size
.cy
= h
;
4126 surface
->Flags
&= ~SFLAG_DS_LOCATIONS
;
4127 surface
->Flags
|= location
;
4130 /* Context activation is done by the caller. */
4131 void surface_load_ds_location(IWineD3DSurfaceImpl
*surface
, struct wined3d_context
*context
, DWORD location
)
4133 IWineD3DDeviceImpl
*device
= surface
->resource
.device
;
4134 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
4136 TRACE("surface %p, new location %#x.\n", surface
, location
);
4138 /* TODO: Make this work for modes other than FBO */
4139 if (wined3d_settings
.offscreen_rendering_mode
!= ORM_FBO
) return;
4141 if (!(surface
->Flags
& location
))
4143 surface
->ds_current_size
.cx
= 0;
4144 surface
->ds_current_size
.cy
= 0;
4147 if (surface
->ds_current_size
.cx
== surface
->currentDesc
.Width
4148 && surface
->ds_current_size
.cy
== surface
->currentDesc
.Height
)
4150 TRACE("Location (%#x) is already up to date.\n", location
);
4154 if (surface
->current_renderbuffer
)
4156 FIXME("Not supported with fixed up depth stencil.\n");
4160 if (!(surface
->Flags
& SFLAG_LOCATIONS
))
4162 FIXME("No up to date depth stencil location.\n");
4163 surface
->Flags
|= location
;
4167 if (location
== SFLAG_DS_OFFSCREEN
)
4169 GLint old_binding
= 0;
4172 TRACE("Copying onscreen depth buffer to depth texture.\n");
4176 if (!device
->depth_blt_texture
)
4178 glGenTextures(1, &device
->depth_blt_texture
);
4181 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4182 * directly on the FBO texture. That's because we need to flip. */
4183 context_bind_fbo(context
, GL_FRAMEBUFFER
, NULL
);
4184 if (surface
->texture_target
== GL_TEXTURE_RECTANGLE_ARB
)
4186 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB
, &old_binding
);
4187 bind_target
= GL_TEXTURE_RECTANGLE_ARB
;
4191 glGetIntegerv(GL_TEXTURE_BINDING_2D
, &old_binding
);
4192 bind_target
= GL_TEXTURE_2D
;
4194 glBindTexture(bind_target
, device
->depth_blt_texture
);
4195 glCopyTexImage2D(bind_target
, surface
->texture_level
, surface
->resource
.format_desc
->glInternal
,
4196 0, 0, surface
->currentDesc
.Width
, surface
->currentDesc
.Height
, 0);
4197 glTexParameteri(bind_target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
4198 glTexParameteri(bind_target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
4199 glTexParameteri(bind_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
4200 glTexParameteri(bind_target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
4201 glTexParameteri(bind_target
, GL_TEXTURE_WRAP_R
, GL_CLAMP_TO_EDGE
);
4202 glTexParameteri(bind_target
, GL_DEPTH_TEXTURE_MODE_ARB
, GL_LUMINANCE
);
4203 glBindTexture(bind_target
, old_binding
);
4205 /* Setup the destination */
4206 if (!device
->depth_blt_rb
)
4208 gl_info
->fbo_ops
.glGenRenderbuffers(1, &device
->depth_blt_rb
);
4209 checkGLcall("glGenRenderbuffersEXT");
4211 if (device
->depth_blt_rb_w
!= surface
->currentDesc
.Width
4212 || device
->depth_blt_rb_h
!= surface
->currentDesc
.Height
)
4214 gl_info
->fbo_ops
.glBindRenderbuffer(GL_RENDERBUFFER
, device
->depth_blt_rb
);
4215 checkGLcall("glBindRenderbufferEXT");
4216 gl_info
->fbo_ops
.glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGBA8
,
4217 surface
->currentDesc
.Width
, surface
->currentDesc
.Height
);
4218 checkGLcall("glRenderbufferStorageEXT");
4219 device
->depth_blt_rb_w
= surface
->currentDesc
.Width
;
4220 device
->depth_blt_rb_h
= surface
->currentDesc
.Height
;
4223 context_bind_fbo(context
, GL_FRAMEBUFFER
, &context
->dst_fbo
);
4224 gl_info
->fbo_ops
.glFramebufferRenderbuffer(GL_FRAMEBUFFER
,
4225 GL_COLOR_ATTACHMENT0
, GL_RENDERBUFFER
, device
->depth_blt_rb
);
4226 checkGLcall("glFramebufferRenderbufferEXT");
4227 context_attach_depth_stencil_fbo(context
, GL_FRAMEBUFFER
, surface
, FALSE
);
4229 /* Do the actual blit */
4230 surface_depth_blt(surface
, gl_info
, device
->depth_blt_texture
,
4231 surface
->currentDesc
.Width
, surface
->currentDesc
.Height
, bind_target
);
4232 checkGLcall("depth_blt");
4234 if (context
->current_fbo
) context_bind_fbo(context
, GL_FRAMEBUFFER
, &context
->current_fbo
->id
);
4235 else context_bind_fbo(context
, GL_FRAMEBUFFER
, NULL
);
4239 if (wined3d_settings
.strict_draw_ordering
) wglFlush(); /* Flush to ensure ordering across contexts. */
4241 else if (location
== SFLAG_DS_ONSCREEN
)
4243 TRACE("Copying depth texture to onscreen depth buffer.\n");
4247 context_bind_fbo(context
, GL_FRAMEBUFFER
, NULL
);
4248 surface_depth_blt(surface
, gl_info
, surface
->texture_name
,
4249 surface
->currentDesc
.Width
, surface
->currentDesc
.Height
, surface
->texture_target
);
4250 checkGLcall("depth_blt");
4252 if (context
->current_fbo
) context_bind_fbo(context
, GL_FRAMEBUFFER
, &context
->current_fbo
->id
);
4256 if (wined3d_settings
.strict_draw_ordering
) wglFlush(); /* Flush to ensure ordering across contexts. */
4260 ERR("Invalid location (%#x) specified.\n", location
);
4263 surface
->Flags
|= location
;
4264 surface
->ds_current_size
.cx
= surface
->currentDesc
.Width
;
4265 surface
->ds_current_size
.cy
= surface
->currentDesc
.Height
;
4268 static void WINAPI
IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface
*iface
, DWORD flag
, BOOL persistent
) {
4269 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
4270 IWineD3DBaseTexture
*texture
;
4271 IWineD3DSurfaceImpl
*overlay
;
4273 TRACE("(%p)->(%s, %s)\n", iface
, debug_surflocation(flag
),
4274 persistent
? "TRUE" : "FALSE");
4276 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
)
4278 if (surface_is_offscreen(This
))
4280 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4281 if (flag
& (SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
)) flag
|= (SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
);
4285 TRACE("Surface %p is an onscreen surface\n", iface
);
4290 if(((This
->Flags
& SFLAG_INTEXTURE
) && !(flag
& SFLAG_INTEXTURE
)) ||
4291 ((This
->Flags
& SFLAG_INSRGBTEX
) && !(flag
& SFLAG_INSRGBTEX
))) {
4292 if (IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&texture
) == WINED3D_OK
) {
4293 TRACE("Passing to container\n");
4294 IWineD3DBaseTexture_SetDirty(texture
, TRUE
);
4295 IWineD3DBaseTexture_Release(texture
);
4298 This
->Flags
&= ~SFLAG_LOCATIONS
;
4299 This
->Flags
|= flag
;
4301 /* Redraw emulated overlays, if any */
4302 if(flag
& SFLAG_INDRAWABLE
&& !list_empty(&This
->overlays
)) {
4303 LIST_FOR_EACH_ENTRY(overlay
, &This
->overlays
, IWineD3DSurfaceImpl
, overlay_entry
) {
4304 IWineD3DSurface_DrawOverlay((IWineD3DSurface
*) overlay
);
4308 if((This
->Flags
& (SFLAG_INTEXTURE
| SFLAG_INSRGBTEX
)) && (flag
& (SFLAG_INTEXTURE
| SFLAG_INSRGBTEX
))) {
4309 if (IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&texture
) == WINED3D_OK
) {
4310 TRACE("Passing to container\n");
4311 IWineD3DBaseTexture_SetDirty(texture
, TRUE
);
4312 IWineD3DBaseTexture_Release(texture
);
4315 This
->Flags
&= ~flag
;
4318 if(!(This
->Flags
& SFLAG_LOCATIONS
)) {
4319 ERR("%p: Surface does not have any up to date location\n", This
);
4323 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl
*This
, const RECT
*rect_in
)
4325 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
4326 IWineD3DSwapChainImpl
*swapchain
;
4327 struct wined3d_context
*context
;
4328 RECT src_rect
, dst_rect
;
4330 surface_get_rect(This
, rect_in
, &src_rect
);
4332 context
= context_acquire(device
, This
, CTXUSAGE_BLIT
);
4333 if (context
->render_offscreen
)
4335 dst_rect
.left
= src_rect
.left
;
4336 dst_rect
.right
= src_rect
.right
;
4337 dst_rect
.top
= src_rect
.bottom
;
4338 dst_rect
.bottom
= src_rect
.top
;
4342 dst_rect
= src_rect
;
4345 device
->blitter
->set_shader((IWineD3DDevice
*) device
, This
);
4348 draw_textured_quad(This
, &src_rect
, &dst_rect
, WINED3DTEXF_POINT
);
4351 device
->blitter
->set_shader((IWineD3DDevice
*) device
, This
);
4353 swapchain
= (This
->Flags
& SFLAG_SWAPCHAIN
) ? (IWineD3DSwapChainImpl
*)This
->container
: NULL
;
4354 if (wined3d_settings
.strict_draw_ordering
|| (swapchain
4355 && (This
== swapchain
->front_buffer
|| swapchain
->num_contexts
> 1)))
4356 wglFlush(); /* Flush to ensure ordering across contexts. */
4358 context_release(context
);
4361 /*****************************************************************************
4362 * IWineD3DSurface::LoadLocation
4364 * Copies the current surface data from wherever it is to the requested
4365 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4366 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4367 * multiple locations, the gl texture is preferred over the drawable, which is
4368 * preferred over system memory. The PBO counts as system memory. If rect is
4369 * not NULL, only the specified rectangle is copied (only supported for
4370 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4371 * location is marked up to date after the copy.
4374 * flag: Surface location flag to be updated
4375 * rect: rectangle to be copied
4378 * WINED3D_OK on success
4379 * WINED3DERR_DEVICELOST on an internal error
4381 *****************************************************************************/
4382 static HRESULT WINAPI
IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface
*iface
, DWORD flag
, const RECT
*rect
) {
4383 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
4384 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
4385 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
4386 struct wined3d_format_desc desc
;
4387 CONVERT_TYPES convert
;
4388 int width
, pitch
, outpitch
;
4390 BOOL drawable_read_ok
= TRUE
;
4391 BOOL in_fbo
= FALSE
;
4393 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
)
4395 if (surface_is_offscreen(This
))
4397 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4398 * Prefer SFLAG_INTEXTURE. */
4399 if (flag
== SFLAG_INDRAWABLE
) flag
= SFLAG_INTEXTURE
;
4400 drawable_read_ok
= FALSE
;
4405 TRACE("Surface %p is an onscreen surface\n", iface
);
4409 TRACE("(%p)->(%s, %p)\n", iface
, debug_surflocation(flag
), rect
);
4411 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
4414 if(This
->Flags
& flag
) {
4415 TRACE("Location already up to date\n");
4419 if(!(This
->Flags
& SFLAG_LOCATIONS
)) {
4420 ERR("%p: Surface does not have any up to date location\n", This
);
4421 This
->Flags
|= SFLAG_LOST
;
4422 return WINED3DERR_DEVICELOST
;
4425 if(flag
== SFLAG_INSYSMEM
) {
4426 surface_prepare_system_memory(This
);
4428 /* Download the surface to system memory */
4429 if (This
->Flags
& (SFLAG_INTEXTURE
| SFLAG_INSRGBTEX
))
4431 struct wined3d_context
*context
= NULL
;
4433 if (!device
->isInDraw
) context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
4435 surface_bind_and_dirtify(This
, !(This
->Flags
& SFLAG_INTEXTURE
));
4436 surface_download_data(This
, gl_info
);
4438 if (context
) context_release(context
);
4442 /* Note: It might be faster to download into a texture first. */
4443 read_from_framebuffer(This
, rect
,
4444 This
->resource
.allocatedMemory
,
4445 IWineD3DSurface_GetPitch(iface
));
4447 } else if(flag
== SFLAG_INDRAWABLE
) {
4448 if(This
->Flags
& SFLAG_INTEXTURE
) {
4449 surface_blt_to_drawable(This
, rect
);
4452 if((This
->Flags
& SFLAG_LOCATIONS
) == SFLAG_INSRGBTEX
) {
4453 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4454 * values, otherwise we get incorrect values in the target. For now go the slow way
4455 * via a system memory copy
4457 IWineD3DSurfaceImpl_LoadLocation(iface
, SFLAG_INSYSMEM
, rect
);
4460 d3dfmt_get_conv(This
, FALSE
/* We need color keying */, FALSE
/* We won't use textures */, &desc
, &convert
);
4462 /* The width is in 'length' not in bytes */
4463 width
= This
->currentDesc
.Width
;
4464 pitch
= IWineD3DSurface_GetPitch(iface
);
4466 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4467 * but it isn't set (yet) in all cases it is getting called. */
4468 if ((convert
!= NO_CONVERSION
) && (This
->Flags
& SFLAG_PBO
))
4470 struct wined3d_context
*context
= NULL
;
4472 TRACE("Removing the pbo attached to surface %p\n", This
);
4474 if (!device
->isInDraw
) context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
4475 surface_remove_pbo(This
, gl_info
);
4476 if (context
) context_release(context
);
4479 if((convert
!= NO_CONVERSION
) && This
->resource
.allocatedMemory
) {
4480 int height
= This
->currentDesc
.Height
;
4481 byte_count
= desc
.conv_byte_count
;
4483 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4484 outpitch
= width
* byte_count
;
4485 outpitch
= (outpitch
+ device
->surface_alignment
- 1) & ~(device
->surface_alignment
- 1);
4487 mem
= HeapAlloc(GetProcessHeap(), 0, outpitch
* height
);
4489 ERR("Out of memory %d, %d!\n", outpitch
, height
);
4490 return WINED3DERR_OUTOFVIDEOMEMORY
;
4492 d3dfmt_convert_surface(This
->resource
.allocatedMemory
, mem
, pitch
, width
, height
, outpitch
, convert
, This
);
4494 This
->Flags
|= SFLAG_CONVERTED
;
4496 This
->Flags
&= ~SFLAG_CONVERTED
;
4497 mem
= This
->resource
.allocatedMemory
;
4498 byte_count
= desc
.byte_count
;
4501 flush_to_framebuffer_drawpixels(This
, desc
.glFormat
, desc
.glType
, byte_count
, mem
);
4503 /* Don't delete PBO memory */
4504 if((mem
!= This
->resource
.allocatedMemory
) && !(This
->Flags
& SFLAG_PBO
))
4505 HeapFree(GetProcessHeap(), 0, mem
);
4507 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4508 if (drawable_read_ok
&& (This
->Flags
& SFLAG_INDRAWABLE
)) {
4509 read_from_framebuffer_texture(This
, flag
== SFLAG_INSRGBTEX
);
4513 /* Upload from system memory */
4514 BOOL srgb
= flag
== SFLAG_INSRGBTEX
;
4515 struct wined3d_context
*context
= NULL
;
4517 d3dfmt_get_conv(This
, TRUE
/* We need color keying */, TRUE
/* We will use textures */,
4521 if((This
->Flags
& (SFLAG_INTEXTURE
| SFLAG_INSYSMEM
)) == SFLAG_INTEXTURE
) {
4522 /* Performance warning ... */
4523 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This
);
4524 IWineD3DSurfaceImpl_LoadLocation(iface
, SFLAG_INSYSMEM
, rect
);
4527 if((This
->Flags
& (SFLAG_INSRGBTEX
| SFLAG_INSYSMEM
)) == SFLAG_INSRGBTEX
) {
4528 /* Performance warning ... */
4529 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This
);
4530 IWineD3DSurfaceImpl_LoadLocation(iface
, SFLAG_INSYSMEM
, rect
);
4533 if(!(This
->Flags
& SFLAG_INSYSMEM
)) {
4534 /* Should not happen */
4535 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4536 /* Lets hope we get it from somewhere... */
4537 IWineD3DSurfaceImpl_LoadLocation(iface
, SFLAG_INSYSMEM
, rect
);
4540 if (!device
->isInDraw
) context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
4542 surface_prepare_texture(This
, gl_info
, srgb
);
4543 surface_bind_and_dirtify(This
, srgb
);
4545 if(This
->CKeyFlags
& WINEDDSD_CKSRCBLT
) {
4546 This
->Flags
|= SFLAG_GLCKEY
;
4547 This
->glCKey
= This
->SrcBltCKey
;
4549 else This
->Flags
&= ~SFLAG_GLCKEY
;
4551 /* The width is in 'length' not in bytes */
4552 width
= This
->currentDesc
.Width
;
4553 pitch
= IWineD3DSurface_GetPitch(iface
);
4555 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4556 * but it isn't set (yet) in all cases it is getting called. */
4557 if((convert
!= NO_CONVERSION
) && (This
->Flags
& SFLAG_PBO
)) {
4558 TRACE("Removing the pbo attached to surface %p\n", This
);
4559 surface_remove_pbo(This
, gl_info
);
4563 /* This code is entered for texture formats which need a fixup. */
4564 int height
= This
->currentDesc
.Height
;
4566 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4567 outpitch
= width
* desc
.conv_byte_count
;
4568 outpitch
= (outpitch
+ device
->surface_alignment
- 1) & ~(device
->surface_alignment
- 1);
4570 mem
= HeapAlloc(GetProcessHeap(), 0, outpitch
* height
);
4572 ERR("Out of memory %d, %d!\n", outpitch
, height
);
4573 if (context
) context_release(context
);
4574 return WINED3DERR_OUTOFVIDEOMEMORY
;
4576 desc
.convert(This
->resource
.allocatedMemory
, mem
, pitch
, width
, height
);
4577 } else if((convert
!= NO_CONVERSION
) && This
->resource
.allocatedMemory
) {
4578 /* This code is only entered for color keying fixups */
4579 int height
= This
->currentDesc
.Height
;
4581 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4582 outpitch
= width
* desc
.conv_byte_count
;
4583 outpitch
= (outpitch
+ device
->surface_alignment
- 1) & ~(device
->surface_alignment
- 1);
4585 mem
= HeapAlloc(GetProcessHeap(), 0, outpitch
* height
);
4587 ERR("Out of memory %d, %d!\n", outpitch
, height
);
4588 if (context
) context_release(context
);
4589 return WINED3DERR_OUTOFVIDEOMEMORY
;
4591 d3dfmt_convert_surface(This
->resource
.allocatedMemory
, mem
, pitch
, width
, height
, outpitch
, convert
, This
);
4593 mem
= This
->resource
.allocatedMemory
;
4596 /* Make sure the correct pitch is used */
4598 glPixelStorei(GL_UNPACK_ROW_LENGTH
, width
);
4601 if (mem
|| (This
->Flags
& SFLAG_PBO
))
4602 surface_upload_data(This
, gl_info
, &desc
, srgb
, mem
);
4604 /* Restore the default pitch */
4606 glPixelStorei(GL_UNPACK_ROW_LENGTH
, 0);
4609 if (context
) context_release(context
);
4611 /* Don't delete PBO memory */
4612 if((mem
!= This
->resource
.allocatedMemory
) && !(This
->Flags
& SFLAG_PBO
))
4613 HeapFree(GetProcessHeap(), 0, mem
);
4618 This
->Flags
|= flag
;
4621 if (in_fbo
&& (This
->Flags
& (SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
))) {
4622 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4623 This
->Flags
|= (SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
);
4629 static HRESULT WINAPI
IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface
*iface
, IWineD3DBase
*container
)
4631 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
4632 IWineD3DSwapChain
*swapchain
= NULL
;
4634 /* Update the drawable size method */
4636 IWineD3DBase_QueryInterface(container
, &IID_IWineD3DSwapChain
, (void **) &swapchain
);
4639 This
->get_drawable_size
= get_drawable_size_swapchain
;
4640 IWineD3DSwapChain_Release(swapchain
);
4641 } else if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) {
4642 switch(wined3d_settings
.offscreen_rendering_mode
) {
4643 case ORM_FBO
: This
->get_drawable_size
= get_drawable_size_fbo
; break;
4644 case ORM_BACKBUFFER
: This
->get_drawable_size
= get_drawable_size_backbuffer
; break;
4648 return IWineD3DBaseSurfaceImpl_SetContainer(iface
, container
);
4651 static WINED3DSURFTYPE WINAPI
IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface
*iface
) {
4652 return SURFACE_OPENGL
;
4655 static HRESULT WINAPI
IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface
*iface
) {
4656 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
4659 /* If there's no destination surface there is nothing to do */
4660 if(!This
->overlay_dest
) return WINED3D_OK
;
4662 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4663 * update the overlay. Prevent an endless recursion
4665 if(This
->overlay_dest
->Flags
& SFLAG_INOVERLAYDRAW
) {
4668 This
->overlay_dest
->Flags
|= SFLAG_INOVERLAYDRAW
;
4669 hr
= IWineD3DSurfaceImpl_Blt((IWineD3DSurface
*) This
->overlay_dest
, &This
->overlay_destrect
,
4670 iface
, &This
->overlay_srcrect
, WINEDDBLT_WAIT
,
4671 NULL
, WINED3DTEXF_LINEAR
);
4672 This
->overlay_dest
->Flags
&= ~SFLAG_INOVERLAYDRAW
;
4677 BOOL
surface_is_offscreen(IWineD3DSurfaceImpl
*surface
)
4679 IWineD3DSwapChainImpl
*swapchain
= (IWineD3DSwapChainImpl
*)surface
->container
;
4681 /* Not on a swapchain - must be offscreen */
4682 if (!(surface
->Flags
& SFLAG_SWAPCHAIN
)) return TRUE
;
4684 /* The front buffer is always onscreen */
4685 if (surface
== swapchain
->front_buffer
) return FALSE
;
4687 /* If the swapchain is rendered to an FBO, the backbuffer is
4688 * offscreen, otherwise onscreen */
4689 return swapchain
->render_to_fbo
;
4692 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl
=
4695 IWineD3DBaseSurfaceImpl_QueryInterface
,
4696 IWineD3DBaseSurfaceImpl_AddRef
,
4697 IWineD3DSurfaceImpl_Release
,
4698 /* IWineD3DResource */
4699 IWineD3DBaseSurfaceImpl_GetParent
,
4700 IWineD3DBaseSurfaceImpl_SetPrivateData
,
4701 IWineD3DBaseSurfaceImpl_GetPrivateData
,
4702 IWineD3DBaseSurfaceImpl_FreePrivateData
,
4703 IWineD3DBaseSurfaceImpl_SetPriority
,
4704 IWineD3DBaseSurfaceImpl_GetPriority
,
4705 IWineD3DSurfaceImpl_PreLoad
,
4706 IWineD3DSurfaceImpl_UnLoad
,
4707 IWineD3DBaseSurfaceImpl_GetType
,
4708 /* IWineD3DSurface */
4709 IWineD3DBaseSurfaceImpl_GetContainer
,
4710 IWineD3DBaseSurfaceImpl_GetDesc
,
4711 IWineD3DSurfaceImpl_LockRect
,
4712 IWineD3DSurfaceImpl_UnlockRect
,
4713 IWineD3DSurfaceImpl_GetDC
,
4714 IWineD3DSurfaceImpl_ReleaseDC
,
4715 IWineD3DSurfaceImpl_Flip
,
4716 IWineD3DSurfaceImpl_Blt
,
4717 IWineD3DBaseSurfaceImpl_GetBltStatus
,
4718 IWineD3DBaseSurfaceImpl_GetFlipStatus
,
4719 IWineD3DBaseSurfaceImpl_IsLost
,
4720 IWineD3DBaseSurfaceImpl_Restore
,
4721 IWineD3DSurfaceImpl_BltFast
,
4722 IWineD3DBaseSurfaceImpl_GetPalette
,
4723 IWineD3DBaseSurfaceImpl_SetPalette
,
4724 IWineD3DSurfaceImpl_RealizePalette
,
4725 IWineD3DBaseSurfaceImpl_SetColorKey
,
4726 IWineD3DBaseSurfaceImpl_GetPitch
,
4727 IWineD3DSurfaceImpl_SetMem
,
4728 IWineD3DBaseSurfaceImpl_SetOverlayPosition
,
4729 IWineD3DBaseSurfaceImpl_GetOverlayPosition
,
4730 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder
,
4731 IWineD3DBaseSurfaceImpl_UpdateOverlay
,
4732 IWineD3DBaseSurfaceImpl_SetClipper
,
4733 IWineD3DBaseSurfaceImpl_GetClipper
,
4735 IWineD3DSurfaceImpl_LoadTexture
,
4736 IWineD3DSurfaceImpl_BindTexture
,
4737 IWineD3DSurfaceImpl_SaveSnapshot
,
4738 IWineD3DSurfaceImpl_SetContainer
,
4739 IWineD3DBaseSurfaceImpl_GetData
,
4740 IWineD3DSurfaceImpl_SetFormat
,
4741 IWineD3DSurfaceImpl_PrivateSetup
,
4742 IWineD3DSurfaceImpl_ModifyLocation
,
4743 IWineD3DSurfaceImpl_LoadLocation
,
4744 IWineD3DSurfaceImpl_GetImplType
,
4745 IWineD3DSurfaceImpl_DrawOverlay
4748 static HRESULT
ffp_blit_alloc(IWineD3DDevice
*iface
) { return WINED3D_OK
; }
4749 /* Context activation is done by the caller. */
4750 static void ffp_blit_free(IWineD3DDevice
*iface
) { }
4752 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
4753 /* Context activation is done by the caller. */
4754 static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl
*surface
, const struct wined3d_gl_info
*gl_info
)
4757 BOOL colorkey_active
= (surface
->CKeyFlags
& WINEDDSD_CKSRCBLT
) ? TRUE
: FALSE
;
4759 d3dfmt_p8_init_palette(surface
, table
, colorkey_active
);
4761 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
4763 GL_EXTCALL(glColorTableEXT(surface
->texture_target
, GL_RGBA
, 256, GL_RGBA
, GL_UNSIGNED_BYTE
, table
));
4767 /* Context activation is done by the caller. */
4768 static HRESULT
ffp_blit_set(IWineD3DDevice
*iface
, IWineD3DSurfaceImpl
*surface
)
4770 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) iface
;
4771 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
4772 enum complex_fixup fixup
= get_complex_fixup(surface
->resource
.format_desc
->color_fixup
);
4774 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
4775 * else the surface is converted in software at upload time in LoadLocation.
4777 if(fixup
== COMPLEX_FIXUP_P8
&& gl_info
->supported
[EXT_PALETTED_TEXTURE
])
4778 ffp_blit_p8_upload_palette(surface
, gl_info
);
4781 glEnable(surface
->texture_target
);
4782 checkGLcall("glEnable(surface->texture_target)");
4787 /* Context activation is done by the caller. */
4788 static void ffp_blit_unset(IWineD3DDevice
*iface
)
4790 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) iface
;
4791 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
4794 glDisable(GL_TEXTURE_2D
);
4795 checkGLcall("glDisable(GL_TEXTURE_2D)");
4796 if (gl_info
->supported
[ARB_TEXTURE_CUBE_MAP
])
4798 glDisable(GL_TEXTURE_CUBE_MAP_ARB
);
4799 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4801 if (gl_info
->supported
[ARB_TEXTURE_RECTANGLE
])
4803 glDisable(GL_TEXTURE_RECTANGLE_ARB
);
4804 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4809 static BOOL
ffp_blit_supported(const struct wined3d_gl_info
*gl_info
, enum blit_operation blit_op
,
4810 const RECT
*src_rect
, DWORD src_usage
, WINED3DPOOL src_pool
,
4811 const struct wined3d_format_desc
*src_format_desc
,
4812 const RECT
*dst_rect
, DWORD dst_usage
, WINED3DPOOL dst_pool
,
4813 const struct wined3d_format_desc
*dst_format_desc
)
4815 enum complex_fixup src_fixup
;
4817 if (blit_op
== BLIT_OP_COLOR_FILL
)
4819 if (!(dst_usage
& WINED3DUSAGE_RENDERTARGET
))
4821 TRACE("Color fill not supported\n");
4828 src_fixup
= get_complex_fixup(src_format_desc
->color_fixup
);
4829 if (TRACE_ON(d3d_surface
) && TRACE_ON(d3d
))
4831 TRACE("Checking support for fixup:\n");
4832 dump_color_fixup_desc(src_format_desc
->color_fixup
);
4835 if (blit_op
!= BLIT_OP_BLIT
)
4837 TRACE("Unsupported blit_op=%d\n", blit_op
);
4841 if (!is_identity_fixup(dst_format_desc
->color_fixup
))
4843 TRACE("Destination fixups are not supported\n");
4847 if (src_fixup
== COMPLEX_FIXUP_P8
&& gl_info
->supported
[EXT_PALETTED_TEXTURE
])
4849 TRACE("P8 fixup supported\n");
4853 /* We only support identity conversions. */
4854 if (is_identity_fixup(src_format_desc
->color_fixup
))
4860 TRACE("[FAILED]\n");
4864 static HRESULT
ffp_blit_color_fill(IWineD3DDeviceImpl
*device
, IWineD3DSurfaceImpl
*dst_surface
, const RECT
*dst_rect
, DWORD fill_color
)
4866 return IWineD3DDeviceImpl_ClearSurface(device
, dst_surface
, 1 /* Number of rectangles */,
4867 (const WINED3DRECT
*)dst_rect
, WINED3DCLEAR_TARGET
, fill_color
, 0.0f
/* Z */, 0 /* Stencil */);
4870 const struct blit_shader ffp_blit
= {
4879 static HRESULT
cpu_blit_alloc(IWineD3DDevice
*iface
)
4884 /* Context activation is done by the caller. */
4885 static void cpu_blit_free(IWineD3DDevice
*iface
)
4889 /* Context activation is done by the caller. */
4890 static HRESULT
cpu_blit_set(IWineD3DDevice
*iface
, IWineD3DSurfaceImpl
*surface
)
4895 /* Context activation is done by the caller. */
4896 static void cpu_blit_unset(IWineD3DDevice
*iface
)
4900 static BOOL
cpu_blit_supported(const struct wined3d_gl_info
*gl_info
, enum blit_operation blit_op
,
4901 const RECT
*src_rect
, DWORD src_usage
, WINED3DPOOL src_pool
,
4902 const struct wined3d_format_desc
*src_format_desc
,
4903 const RECT
*dst_rect
, DWORD dst_usage
, WINED3DPOOL dst_pool
,
4904 const struct wined3d_format_desc
*dst_format_desc
)
4906 if (blit_op
== BLIT_OP_COLOR_FILL
)
4914 static HRESULT
cpu_blit_color_fill(IWineD3DDeviceImpl
*device
, IWineD3DSurfaceImpl
*dst_surface
, const RECT
*dst_rect
, DWORD fill_color
)
4917 memset(&BltFx
, 0, sizeof(BltFx
));
4918 BltFx
.dwSize
= sizeof(BltFx
);
4919 BltFx
.u5
.dwFillColor
= color_convert_argb_to_fmt(fill_color
, dst_surface
->resource
.format_desc
->format
);
4920 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface
*)dst_surface
, dst_rect
, NULL
, NULL
, WINEDDBLT_COLORFILL
, &BltFx
, WINED3DTEXF_POINT
);
4923 const struct blit_shader cpu_blit
= {
4932 static BOOL
fbo_blit_supported(const struct wined3d_gl_info
*gl_info
, enum blit_operation blit_op
,
4933 const RECT
*src_rect
, DWORD src_usage
, WINED3DPOOL src_pool
,
4934 const struct wined3d_format_desc
*src_format_desc
,
4935 const RECT
*dst_rect
, DWORD dst_usage
, WINED3DPOOL dst_pool
,
4936 const struct wined3d_format_desc
*dst_format_desc
)
4938 if ((wined3d_settings
.offscreen_rendering_mode
!= ORM_FBO
) || !gl_info
->fbo_ops
.glBlitFramebuffer
)
4941 /* We only support blitting. Things like color keying / color fill should
4942 * be handled by other blitters.
4944 if (blit_op
!= BLIT_OP_BLIT
)
4947 /* Source and/or destination need to be on the GL side */
4948 if (src_pool
== WINED3DPOOL_SYSTEMMEM
|| dst_pool
== WINED3DPOOL_SYSTEMMEM
)
4951 if(!((src_format_desc
->Flags
& WINED3DFMT_FLAG_FBO_ATTACHABLE
) || (src_usage
& WINED3DUSAGE_RENDERTARGET
))
4952 && ((dst_format_desc
->Flags
& WINED3DFMT_FLAG_FBO_ATTACHABLE
) || (dst_usage
& WINED3DUSAGE_RENDERTARGET
)))
4955 if (!is_identity_fixup(src_format_desc
->color_fixup
) ||
4956 !is_identity_fixup(dst_format_desc
->color_fixup
))
4959 if (!(src_format_desc
->format
== dst_format_desc
->format
4960 || (is_identity_fixup(src_format_desc
->color_fixup
)
4961 && is_identity_fixup(dst_format_desc
->color_fixup
))))