2 * Copyright 2002-2005 Jason Edmeades
3 * Copyright 2002-2005 Raphael Junqueira
4 * Copyright 2005 Oliver Stieber
5 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
6 * Copyright 2013 Stefan Dösinger for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
25 #include "wined3d_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
28 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf
);
30 static BOOL
volume_prepare_system_memory(struct wined3d_volume
*volume
)
32 if (volume
->resource
.heap_memory
)
35 if (!wined3d_resource_allocate_sysmem(&volume
->resource
))
37 ERR("Failed to allocate system memory.\n");
43 /* Context activation is done by the caller. Context may be NULL in
44 * WINED3D_NO3D mode. */
45 BOOL
wined3d_volume_prepare_location(struct wined3d_volume
*volume
,
46 struct wined3d_context
*context
, DWORD location
)
48 struct wined3d_texture
*texture
= volume
->container
;
52 case WINED3D_LOCATION_SYSMEM
:
53 return volume_prepare_system_memory(volume
);
55 case WINED3D_LOCATION_BUFFER
:
56 wined3d_texture_prepare_buffer_object(texture
, volume
->texture_level
, context
->gl_info
);
59 case WINED3D_LOCATION_TEXTURE_RGB
:
60 wined3d_texture_prepare_texture(texture
, context
, FALSE
);
63 case WINED3D_LOCATION_TEXTURE_SRGB
:
64 wined3d_texture_prepare_texture(texture
, context
, TRUE
);
68 ERR("Invalid location %s.\n", wined3d_debug_location(location
));
73 /* This call just uploads data, the caller is responsible for binding the
75 /* Context activation is done by the caller. */
76 void wined3d_volume_upload_data(struct wined3d_volume
*volume
, const struct wined3d_context
*context
,
77 const struct wined3d_const_bo_address
*data
)
79 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
80 struct wined3d_texture
*texture
= volume
->container
;
81 const struct wined3d_format
*format
= texture
->resource
.format
;
82 UINT width
= volume
->resource
.width
;
83 UINT height
= volume
->resource
.height
;
84 UINT depth
= volume
->resource
.depth
;
85 const void *mem
= data
->addr
;
86 void *converted_mem
= NULL
;
88 TRACE("volume %p, context %p, level %u, format %s (%#x).\n",
89 volume
, context
, volume
->texture_level
, debug_d3dformat(format
->id
),
94 UINT dst_row_pitch
, dst_slice_pitch
;
95 UINT src_row_pitch
, src_slice_pitch
;
97 if (data
->buffer_object
)
98 ERR("Loading a converted volume from a PBO.\n");
99 if (texture
->resource
.format_flags
& WINED3DFMT_FLAG_BLOCKS
)
100 ERR("Converting a block-based format.\n");
102 dst_row_pitch
= width
* format
->conv_byte_count
;
103 dst_slice_pitch
= dst_row_pitch
* height
;
105 wined3d_texture_get_pitch(texture
, volume
->texture_level
, &src_row_pitch
, &src_slice_pitch
);
107 converted_mem
= HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch
* depth
);
108 format
->convert(data
->addr
, converted_mem
, src_row_pitch
, src_slice_pitch
,
109 dst_row_pitch
, dst_slice_pitch
, width
, height
, depth
);
113 if (data
->buffer_object
)
115 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, data
->buffer_object
));
116 checkGLcall("glBindBuffer");
119 GL_EXTCALL(glTexSubImage3D(GL_TEXTURE_3D
, volume
->texture_level
, 0, 0, 0,
120 width
, height
, depth
,
121 format
->glFormat
, format
->glType
, mem
));
122 checkGLcall("glTexSubImage3D");
124 if (data
->buffer_object
)
126 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, 0));
127 checkGLcall("glBindBuffer");
130 HeapFree(GetProcessHeap(), 0, converted_mem
);
133 /* Context activation is done by the caller. */
134 static void wined3d_volume_download_data(struct wined3d_volume
*volume
,
135 const struct wined3d_context
*context
, const struct wined3d_bo_address
*data
)
137 const struct wined3d_format
*format
= volume
->container
->resource
.format
;
138 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
142 FIXME("Attempting to download a converted volume, format %s.\n",
143 debug_d3dformat(format
->id
));
147 if (data
->buffer_object
)
149 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER
, data
->buffer_object
));
150 checkGLcall("glBindBuffer");
153 gl_info
->gl_ops
.gl
.p_glGetTexImage(GL_TEXTURE_3D
, volume
->texture_level
,
154 format
->glFormat
, format
->glType
, data
->addr
);
155 checkGLcall("glGetTexImage");
157 if (data
->buffer_object
)
159 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER
, 0));
160 checkGLcall("glBindBuffer");
165 static void wined3d_volume_evict_sysmem(struct wined3d_volume
*volume
)
167 wined3d_resource_free_sysmem(&volume
->resource
);
168 wined3d_texture_invalidate_location(volume
->container
, volume
->texture_level
, WINED3D_LOCATION_SYSMEM
);
171 static DWORD
volume_access_from_location(DWORD location
)
175 case WINED3D_LOCATION_DISCARDED
:
178 case WINED3D_LOCATION_SYSMEM
:
179 return WINED3D_RESOURCE_ACCESS_CPU
;
181 case WINED3D_LOCATION_BUFFER
:
182 case WINED3D_LOCATION_TEXTURE_RGB
:
183 case WINED3D_LOCATION_TEXTURE_SRGB
:
184 return WINED3D_RESOURCE_ACCESS_GPU
;
187 FIXME("Unhandled location %#x.\n", location
);
192 /* Context activation is done by the caller. */
193 static void wined3d_volume_srgb_transfer(struct wined3d_volume
*volume
,
194 struct wined3d_context
*context
, BOOL dest_is_srgb
)
196 struct wined3d_bo_address data
;
197 /* Optimizations are possible, but the effort should be put into either
198 * implementing EXT_SRGB_DECODE in the driver or finding out why we
199 * picked the wrong copy for the original upload and fixing that.
201 * Also keep in mind that we want to avoid using resource.heap_memory
202 * for DEFAULT pool surfaces. */
204 WARN_(d3d_perf
)("Performing slow rgb/srgb volume transfer.\n");
205 data
.buffer_object
= 0;
206 data
.addr
= HeapAlloc(GetProcessHeap(), 0, volume
->resource
.size
);
210 wined3d_texture_bind_and_dirtify(volume
->container
, context
, !dest_is_srgb
);
211 wined3d_volume_download_data(volume
, context
, &data
);
212 wined3d_texture_bind_and_dirtify(volume
->container
, context
, dest_is_srgb
);
213 wined3d_volume_upload_data(volume
, context
, wined3d_const_bo_address(&data
));
215 HeapFree(GetProcessHeap(), 0, data
.addr
);
218 static BOOL
wined3d_volume_can_evict(const struct wined3d_volume
*volume
)
220 struct wined3d_texture
*texture
= volume
->container
;
222 if (texture
->resource
.pool
!= WINED3D_POOL_MANAGED
)
224 if (texture
->download_count
>= 10)
226 if (texture
->resource
.format
->convert
)
232 /* Context activation is done by the caller. */
233 BOOL
wined3d_volume_load_location(struct wined3d_volume
*volume
,
234 struct wined3d_context
*context
, DWORD location
)
236 DWORD required_access
= volume_access_from_location(location
);
237 unsigned int sub_resource_idx
= volume
->texture_level
;
238 struct wined3d_texture
*texture
= volume
->container
;
239 struct wined3d_texture_sub_resource
*sub_resource
;
241 sub_resource
= &texture
->sub_resources
[sub_resource_idx
];
242 TRACE("Volume %p, loading %s, have %s.\n", volume
, wined3d_debug_location(location
),
243 wined3d_debug_location(sub_resource
->locations
));
245 if ((sub_resource
->locations
& location
) == location
)
247 TRACE("Location(s) already up to date.\n");
251 if ((texture
->resource
.access_flags
& required_access
) != required_access
)
253 ERR("Operation requires %#x access, but volume only has %#x.\n",
254 required_access
, texture
->resource
.access_flags
);
258 if (!wined3d_volume_prepare_location(volume
, context
, location
))
261 if (sub_resource
->locations
& WINED3D_LOCATION_DISCARDED
)
263 TRACE("Volume previously discarded, nothing to do.\n");
264 wined3d_texture_validate_location(texture
, sub_resource_idx
, location
);
265 wined3d_texture_invalidate_location(texture
, sub_resource_idx
, WINED3D_LOCATION_DISCARDED
);
271 case WINED3D_LOCATION_TEXTURE_RGB
:
272 case WINED3D_LOCATION_TEXTURE_SRGB
:
273 if (sub_resource
->locations
& WINED3D_LOCATION_SYSMEM
)
275 struct wined3d_const_bo_address data
= {0, volume
->resource
.heap_memory
};
276 wined3d_texture_bind_and_dirtify(texture
, context
,
277 location
== WINED3D_LOCATION_TEXTURE_SRGB
);
278 wined3d_volume_upload_data(volume
, context
, &data
);
280 else if (sub_resource
->locations
& WINED3D_LOCATION_BUFFER
)
282 struct wined3d_const_bo_address data
= {sub_resource
->buffer_object
, NULL
};
283 wined3d_texture_bind_and_dirtify(texture
, context
,
284 location
== WINED3D_LOCATION_TEXTURE_SRGB
);
285 wined3d_volume_upload_data(volume
, context
, &data
);
287 else if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
289 wined3d_volume_srgb_transfer(volume
, context
, TRUE
);
291 else if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_SRGB
)
293 wined3d_volume_srgb_transfer(volume
, context
, FALSE
);
297 FIXME("Implement texture loading from %s.\n", wined3d_debug_location(sub_resource
->locations
));
302 case WINED3D_LOCATION_SYSMEM
:
303 if (sub_resource
->locations
& (WINED3D_LOCATION_TEXTURE_RGB
| WINED3D_LOCATION_TEXTURE_SRGB
))
305 struct wined3d_bo_address data
= {0, volume
->resource
.heap_memory
};
307 if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
308 wined3d_texture_bind_and_dirtify(texture
, context
, FALSE
);
310 wined3d_texture_bind_and_dirtify(texture
, context
, TRUE
);
312 wined3d_volume_download_data(volume
, context
, &data
);
313 ++texture
->download_count
;
317 FIXME("Implement WINED3D_LOCATION_SYSMEM loading from %s.\n",
318 wined3d_debug_location(sub_resource
->locations
));
323 case WINED3D_LOCATION_BUFFER
:
324 if (sub_resource
->locations
& (WINED3D_LOCATION_TEXTURE_RGB
| WINED3D_LOCATION_TEXTURE_SRGB
))
326 struct wined3d_bo_address data
= {sub_resource
->buffer_object
, NULL
};
328 if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
329 wined3d_texture_bind_and_dirtify(texture
, context
, FALSE
);
331 wined3d_texture_bind_and_dirtify(texture
, context
, TRUE
);
333 wined3d_volume_download_data(volume
, context
, &data
);
337 FIXME("Implement WINED3D_LOCATION_BUFFER loading from %s.\n",
338 wined3d_debug_location(sub_resource
->locations
));
344 FIXME("Implement %s loading from %s.\n", wined3d_debug_location(location
),
345 wined3d_debug_location(sub_resource
->locations
));
350 wined3d_texture_validate_location(texture
, sub_resource_idx
, location
);
352 if (location
!= WINED3D_LOCATION_SYSMEM
&& wined3d_volume_can_evict(volume
))
353 wined3d_volume_evict_sysmem(volume
);
358 /* Context activation is done by the caller. */
359 void wined3d_volume_load(struct wined3d_volume
*volume
, struct wined3d_context
*context
, BOOL srgb_mode
)
361 wined3d_texture_prepare_texture(volume
->container
, context
, srgb_mode
);
362 wined3d_volume_load_location(volume
, context
,
363 srgb_mode
? WINED3D_LOCATION_TEXTURE_SRGB
: WINED3D_LOCATION_TEXTURE_RGB
);
366 void wined3d_volume_cleanup(struct wined3d_volume
*volume
)
368 TRACE("volume %p.\n", volume
);
370 resource_cleanup(&volume
->resource
);
373 static void volume_unload(struct wined3d_resource
*resource
)
375 struct wined3d_volume
*volume
= volume_from_resource(resource
);
376 struct wined3d_texture
*texture
= volume
->container
;
377 struct wined3d_device
*device
= texture
->resource
.device
;
378 struct wined3d_context
*context
;
380 if (texture
->resource
.pool
== WINED3D_POOL_DEFAULT
)
381 ERR("Unloading DEFAULT pool volume.\n");
383 TRACE("texture %p.\n", resource
);
385 context
= context_acquire(device
, NULL
);
386 if (wined3d_volume_load_location(volume
, context
, WINED3D_LOCATION_SYSMEM
))
388 wined3d_texture_invalidate_location(texture
, volume
->texture_level
, ~WINED3D_LOCATION_SYSMEM
);
392 ERR("Out of memory when unloading volume %p.\n", volume
);
393 wined3d_texture_validate_location(texture
, volume
->texture_level
, WINED3D_LOCATION_DISCARDED
);
394 wined3d_texture_invalidate_location(texture
, volume
->texture_level
, ~WINED3D_LOCATION_DISCARDED
);
396 context_release(context
);
398 /* The texture name is managed by the container. */
400 resource_unload(resource
);
403 static ULONG
volume_resource_incref(struct wined3d_resource
*resource
)
405 struct wined3d_volume
*volume
= volume_from_resource(resource
);
406 TRACE("Forwarding to container %p.\n", volume
->container
);
408 return wined3d_texture_incref(volume
->container
);
411 static ULONG
volume_resource_decref(struct wined3d_resource
*resource
)
413 struct wined3d_volume
*volume
= volume_from_resource(resource
);
414 TRACE("Forwarding to container %p.\n", volume
->container
);
416 return wined3d_texture_decref(volume
->container
);
419 static HRESULT
volume_resource_sub_resource_map(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
,
420 struct wined3d_map_desc
*map_desc
, const struct wined3d_box
*box
, DWORD flags
)
422 ERR("Not supported on sub-resources.\n");
423 return WINED3DERR_INVALIDCALL
;
426 static HRESULT
volume_resource_sub_resource_unmap(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
)
428 ERR("Not supported on sub-resources.\n");
429 return WINED3DERR_INVALIDCALL
;
432 static const struct wined3d_resource_ops volume_resource_ops
=
434 volume_resource_incref
,
435 volume_resource_decref
,
437 volume_resource_sub_resource_map
,
438 volume_resource_sub_resource_unmap
,
441 HRESULT
wined3d_volume_init(struct wined3d_volume
*volume
, struct wined3d_texture
*container
,
442 const struct wined3d_resource_desc
*desc
, UINT level
)
444 struct wined3d_device
*device
= container
->resource
.device
;
445 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
446 const struct wined3d_format
*format
= wined3d_get_format(gl_info
, desc
->format
);
450 /* TODO: Write tests for other resources and move this check
451 * to resource_init, if applicable. */
452 if (desc
->usage
& WINED3DUSAGE_DYNAMIC
453 && (desc
->pool
== WINED3D_POOL_MANAGED
|| desc
->pool
== WINED3D_POOL_SCRATCH
))
455 WARN("Attempted to create a DYNAMIC texture in pool %s.\n", debug_d3dpool(desc
->pool
));
456 return WINED3DERR_INVALIDCALL
;
459 size
= wined3d_format_calculate_size(format
, device
->surface_alignment
, desc
->width
, desc
->height
, desc
->depth
);
461 if (FAILED(hr
= resource_init(&volume
->resource
, device
, WINED3D_RTYPE_VOLUME
, format
,
462 WINED3D_MULTISAMPLE_NONE
, 0, desc
->usage
, desc
->pool
, desc
->width
, desc
->height
, desc
->depth
,
463 size
, NULL
, &wined3d_null_parent_ops
, &volume_resource_ops
)))
465 WARN("Failed to initialize resource, returning %#x.\n", hr
);
469 volume
->texture_level
= level
;
470 container
->sub_resources
[level
].locations
= WINED3D_LOCATION_DISCARDED
;
472 if (wined3d_texture_use_pbo(container
, gl_info
))
474 wined3d_resource_free_sysmem(&volume
->resource
);
475 volume
->resource
.map_binding
= WINED3D_LOCATION_BUFFER
;
478 volume
->container
= container
;