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 unsigned int width
, height
, depth
;
83 const void *mem
= data
->addr
;
84 void *converted_mem
= NULL
;
86 TRACE("volume %p, context %p, level %u, format %s (%#x).\n",
87 volume
, context
, volume
->texture_level
, debug_d3dformat(format
->id
),
90 width
= wined3d_texture_get_level_width(texture
, volume
->texture_level
);
91 height
= wined3d_texture_get_level_height(texture
, volume
->texture_level
);
92 depth
= wined3d_texture_get_level_depth(texture
, volume
->texture_level
);
96 UINT dst_row_pitch
, dst_slice_pitch
;
97 UINT src_row_pitch
, src_slice_pitch
;
99 if (data
->buffer_object
)
100 ERR("Loading a converted volume from a PBO.\n");
101 if (texture
->resource
.format_flags
& WINED3DFMT_FLAG_BLOCKS
)
102 ERR("Converting a block-based format.\n");
104 dst_row_pitch
= width
* format
->conv_byte_count
;
105 dst_slice_pitch
= dst_row_pitch
* height
;
107 wined3d_texture_get_pitch(texture
, volume
->texture_level
, &src_row_pitch
, &src_slice_pitch
);
109 converted_mem
= HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch
* depth
);
110 format
->convert(data
->addr
, converted_mem
, src_row_pitch
, src_slice_pitch
,
111 dst_row_pitch
, dst_slice_pitch
, width
, height
, depth
);
115 if (data
->buffer_object
)
117 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, data
->buffer_object
));
118 checkGLcall("glBindBuffer");
121 GL_EXTCALL(glTexSubImage3D(GL_TEXTURE_3D
, volume
->texture_level
, 0, 0, 0,
122 width
, height
, depth
,
123 format
->glFormat
, format
->glType
, mem
));
124 checkGLcall("glTexSubImage3D");
126 if (data
->buffer_object
)
128 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, 0));
129 checkGLcall("glBindBuffer");
132 HeapFree(GetProcessHeap(), 0, converted_mem
);
135 /* Context activation is done by the caller. */
136 static void wined3d_volume_download_data(struct wined3d_volume
*volume
,
137 const struct wined3d_context
*context
, const struct wined3d_bo_address
*data
)
139 const struct wined3d_format
*format
= volume
->container
->resource
.format
;
140 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
144 FIXME("Attempting to download a converted volume, format %s.\n",
145 debug_d3dformat(format
->id
));
149 if (data
->buffer_object
)
151 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER
, data
->buffer_object
));
152 checkGLcall("glBindBuffer");
155 gl_info
->gl_ops
.gl
.p_glGetTexImage(GL_TEXTURE_3D
, volume
->texture_level
,
156 format
->glFormat
, format
->glType
, data
->addr
);
157 checkGLcall("glGetTexImage");
159 if (data
->buffer_object
)
161 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER
, 0));
162 checkGLcall("glBindBuffer");
167 static void wined3d_volume_evict_sysmem(struct wined3d_volume
*volume
)
169 wined3d_resource_free_sysmem(&volume
->resource
);
170 wined3d_texture_invalidate_location(volume
->container
, volume
->texture_level
, WINED3D_LOCATION_SYSMEM
);
173 static DWORD
volume_access_from_location(DWORD location
)
177 case WINED3D_LOCATION_DISCARDED
:
180 case WINED3D_LOCATION_SYSMEM
:
181 return WINED3D_RESOURCE_ACCESS_CPU
;
183 case WINED3D_LOCATION_BUFFER
:
184 case WINED3D_LOCATION_TEXTURE_RGB
:
185 case WINED3D_LOCATION_TEXTURE_SRGB
:
186 return WINED3D_RESOURCE_ACCESS_GPU
;
189 FIXME("Unhandled location %#x.\n", location
);
194 /* Context activation is done by the caller. */
195 static void wined3d_volume_srgb_transfer(struct wined3d_volume
*volume
,
196 struct wined3d_context
*context
, BOOL dest_is_srgb
)
198 struct wined3d_bo_address data
;
199 /* Optimizations are possible, but the effort should be put into either
200 * implementing EXT_SRGB_DECODE in the driver or finding out why we
201 * picked the wrong copy for the original upload and fixing that.
203 * Also keep in mind that we want to avoid using resource.heap_memory
204 * for DEFAULT pool surfaces. */
206 WARN_(d3d_perf
)("Performing slow rgb/srgb volume transfer.\n");
207 data
.buffer_object
= 0;
208 data
.addr
= HeapAlloc(GetProcessHeap(), 0, volume
->resource
.size
);
212 wined3d_texture_bind_and_dirtify(volume
->container
, context
, !dest_is_srgb
);
213 wined3d_volume_download_data(volume
, context
, &data
);
214 wined3d_texture_bind_and_dirtify(volume
->container
, context
, dest_is_srgb
);
215 wined3d_volume_upload_data(volume
, context
, wined3d_const_bo_address(&data
));
217 HeapFree(GetProcessHeap(), 0, data
.addr
);
220 static BOOL
wined3d_volume_can_evict(const struct wined3d_volume
*volume
)
222 struct wined3d_texture
*texture
= volume
->container
;
224 if (texture
->resource
.pool
!= WINED3D_POOL_MANAGED
)
226 if (texture
->download_count
>= 10)
228 if (texture
->resource
.format
->convert
)
234 /* Context activation is done by the caller. */
235 BOOL
wined3d_volume_load_location(struct wined3d_volume
*volume
,
236 struct wined3d_context
*context
, DWORD location
)
238 DWORD required_access
= volume_access_from_location(location
);
239 unsigned int sub_resource_idx
= volume
->texture_level
;
240 struct wined3d_texture
*texture
= volume
->container
;
241 struct wined3d_texture_sub_resource
*sub_resource
;
243 sub_resource
= &texture
->sub_resources
[sub_resource_idx
];
244 TRACE("Volume %p, loading %s, have %s.\n", volume
, wined3d_debug_location(location
),
245 wined3d_debug_location(sub_resource
->locations
));
247 if ((sub_resource
->locations
& location
) == location
)
249 TRACE("Location(s) already up to date.\n");
253 if ((texture
->resource
.access_flags
& required_access
) != required_access
)
255 ERR("Operation requires %#x access, but volume only has %#x.\n",
256 required_access
, texture
->resource
.access_flags
);
260 if (!wined3d_volume_prepare_location(volume
, context
, location
))
263 if (sub_resource
->locations
& WINED3D_LOCATION_DISCARDED
)
265 TRACE("Volume previously discarded, nothing to do.\n");
266 wined3d_texture_validate_location(texture
, sub_resource_idx
, location
);
267 wined3d_texture_invalidate_location(texture
, sub_resource_idx
, WINED3D_LOCATION_DISCARDED
);
273 case WINED3D_LOCATION_TEXTURE_RGB
:
274 case WINED3D_LOCATION_TEXTURE_SRGB
:
275 if (sub_resource
->locations
& WINED3D_LOCATION_SYSMEM
)
277 struct wined3d_const_bo_address data
= {0, volume
->resource
.heap_memory
};
278 wined3d_texture_bind_and_dirtify(texture
, context
,
279 location
== WINED3D_LOCATION_TEXTURE_SRGB
);
280 wined3d_volume_upload_data(volume
, context
, &data
);
282 else if (sub_resource
->locations
& WINED3D_LOCATION_BUFFER
)
284 struct wined3d_const_bo_address data
= {sub_resource
->buffer_object
, NULL
};
285 wined3d_texture_bind_and_dirtify(texture
, context
,
286 location
== WINED3D_LOCATION_TEXTURE_SRGB
);
287 wined3d_volume_upload_data(volume
, context
, &data
);
289 else if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
291 wined3d_volume_srgb_transfer(volume
, context
, TRUE
);
293 else if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_SRGB
)
295 wined3d_volume_srgb_transfer(volume
, context
, FALSE
);
299 FIXME("Implement texture loading from %s.\n", wined3d_debug_location(sub_resource
->locations
));
304 case WINED3D_LOCATION_SYSMEM
:
305 if (sub_resource
->locations
& (WINED3D_LOCATION_TEXTURE_RGB
| WINED3D_LOCATION_TEXTURE_SRGB
))
307 struct wined3d_bo_address data
= {0, volume
->resource
.heap_memory
};
309 if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
310 wined3d_texture_bind_and_dirtify(texture
, context
, FALSE
);
312 wined3d_texture_bind_and_dirtify(texture
, context
, TRUE
);
314 wined3d_volume_download_data(volume
, context
, &data
);
315 ++texture
->download_count
;
319 FIXME("Implement WINED3D_LOCATION_SYSMEM loading from %s.\n",
320 wined3d_debug_location(sub_resource
->locations
));
325 case WINED3D_LOCATION_BUFFER
:
326 if (sub_resource
->locations
& (WINED3D_LOCATION_TEXTURE_RGB
| WINED3D_LOCATION_TEXTURE_SRGB
))
328 struct wined3d_bo_address data
= {sub_resource
->buffer_object
, NULL
};
330 if (sub_resource
->locations
& WINED3D_LOCATION_TEXTURE_RGB
)
331 wined3d_texture_bind_and_dirtify(texture
, context
, FALSE
);
333 wined3d_texture_bind_and_dirtify(texture
, context
, TRUE
);
335 wined3d_volume_download_data(volume
, context
, &data
);
339 FIXME("Implement WINED3D_LOCATION_BUFFER loading from %s.\n",
340 wined3d_debug_location(sub_resource
->locations
));
346 FIXME("Implement %s loading from %s.\n", wined3d_debug_location(location
),
347 wined3d_debug_location(sub_resource
->locations
));
352 wined3d_texture_validate_location(texture
, sub_resource_idx
, location
);
354 if (location
!= WINED3D_LOCATION_SYSMEM
&& wined3d_volume_can_evict(volume
))
355 wined3d_volume_evict_sysmem(volume
);
360 void wined3d_volume_cleanup(struct wined3d_volume
*volume
)
362 TRACE("volume %p.\n", volume
);
364 resource_cleanup(&volume
->resource
);
367 static void volume_unload(struct wined3d_resource
*resource
)
369 struct wined3d_volume
*volume
= volume_from_resource(resource
);
370 struct wined3d_texture
*texture
= volume
->container
;
371 struct wined3d_device
*device
= texture
->resource
.device
;
372 struct wined3d_context
*context
;
374 if (texture
->resource
.pool
== WINED3D_POOL_DEFAULT
)
375 ERR("Unloading DEFAULT pool volume.\n");
377 TRACE("texture %p.\n", resource
);
379 context
= context_acquire(device
, NULL
);
380 if (wined3d_volume_load_location(volume
, context
, WINED3D_LOCATION_SYSMEM
))
382 wined3d_texture_invalidate_location(texture
, volume
->texture_level
, ~WINED3D_LOCATION_SYSMEM
);
386 ERR("Out of memory when unloading volume %p.\n", volume
);
387 wined3d_texture_validate_location(texture
, volume
->texture_level
, WINED3D_LOCATION_DISCARDED
);
388 wined3d_texture_invalidate_location(texture
, volume
->texture_level
, ~WINED3D_LOCATION_DISCARDED
);
390 context_release(context
);
392 /* The texture name is managed by the container. */
394 resource_unload(resource
);
397 static ULONG
volume_resource_incref(struct wined3d_resource
*resource
)
399 struct wined3d_volume
*volume
= volume_from_resource(resource
);
400 TRACE("Forwarding to container %p.\n", volume
->container
);
402 return wined3d_texture_incref(volume
->container
);
405 static ULONG
volume_resource_decref(struct wined3d_resource
*resource
)
407 struct wined3d_volume
*volume
= volume_from_resource(resource
);
408 TRACE("Forwarding to container %p.\n", volume
->container
);
410 return wined3d_texture_decref(volume
->container
);
413 static HRESULT
volume_resource_sub_resource_map(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
,
414 struct wined3d_map_desc
*map_desc
, const struct wined3d_box
*box
, DWORD flags
)
416 ERR("Not supported on sub-resources.\n");
417 return WINED3DERR_INVALIDCALL
;
420 static HRESULT
volume_resource_sub_resource_unmap(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
)
422 ERR("Not supported on sub-resources.\n");
423 return WINED3DERR_INVALIDCALL
;
426 static const struct wined3d_resource_ops volume_resource_ops
=
428 volume_resource_incref
,
429 volume_resource_decref
,
431 volume_resource_sub_resource_map
,
432 volume_resource_sub_resource_unmap
,
435 HRESULT
wined3d_volume_init(struct wined3d_volume
*volume
, struct wined3d_texture
*container
,
436 const struct wined3d_resource_desc
*desc
, UINT level
)
438 struct wined3d_device
*device
= container
->resource
.device
;
439 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
440 const struct wined3d_format
*format
= wined3d_get_format(gl_info
, desc
->format
);
444 /* TODO: Write tests for other resources and move this check
445 * to resource_init, if applicable. */
446 if (desc
->usage
& WINED3DUSAGE_DYNAMIC
447 && (desc
->pool
== WINED3D_POOL_MANAGED
|| desc
->pool
== WINED3D_POOL_SCRATCH
))
449 WARN("Attempted to create a DYNAMIC texture in pool %s.\n", debug_d3dpool(desc
->pool
));
450 return WINED3DERR_INVALIDCALL
;
453 size
= wined3d_format_calculate_size(format
, device
->surface_alignment
, desc
->width
, desc
->height
, desc
->depth
);
455 if (FAILED(hr
= resource_init(&volume
->resource
, device
, WINED3D_RTYPE_VOLUME
, format
,
456 WINED3D_MULTISAMPLE_NONE
, 0, desc
->usage
, desc
->pool
, desc
->width
, desc
->height
, desc
->depth
,
457 size
, NULL
, &wined3d_null_parent_ops
, &volume_resource_ops
)))
459 WARN("Failed to initialize resource, returning %#x.\n", hr
);
463 volume
->texture_level
= level
;
464 container
->sub_resources
[level
].locations
= WINED3D_LOCATION_DISCARDED
;
466 if (wined3d_texture_use_pbo(container
, gl_info
))
468 wined3d_resource_free_sysmem(&volume
->resource
);
469 volume
->resource
.map_binding
= WINED3D_LOCATION_BUFFER
;
472 volume
->container
= container
;