wined3d: Merge texture2d_bind() and texture3d_bind() into wined3d_texture_bind().
[wine/multimedia.git] / dlls / wined3d / volume.c
blob037e904689c796a11683e690dedba01d97709402
1 /*
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
23 #include "config.h"
24 #include "wine/port.h"
25 #include "wined3d_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
28 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
30 /* Context activation is done by the caller. */
31 static void volume_bind_and_dirtify(const struct wined3d_volume *volume,
32 struct wined3d_context *context, BOOL srgb)
34 DWORD active_sampler;
36 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
37 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
38 * gl states. The current texture unit should always be a valid one.
40 * To be more specific, this is tricky because we can implicitly be called
41 * from sampler() in state.c. This means we can't touch anything other than
42 * whatever happens to be the currently active texture, or we would risk
43 * marking already applied sampler states dirty again. */
44 active_sampler = context->rev_tex_unit_map[context->active_texture];
46 if (active_sampler != WINED3D_UNMAPPED_STAGE)
47 context_invalidate_state(context, STATE_SAMPLER(active_sampler));
49 wined3d_texture_bind(volume->container, context, srgb);
52 void volume_set_container(struct wined3d_volume *volume, struct wined3d_texture *container)
54 TRACE("volume %p, container %p.\n", volume, container);
56 volume->container = container;
59 static BOOL volume_prepare_system_memory(struct wined3d_volume *volume)
61 if (volume->resource.heap_memory)
62 return TRUE;
64 if (!wined3d_resource_allocate_sysmem(&volume->resource))
66 ERR("Failed to allocate system memory.\n");
67 return FALSE;
69 return TRUE;
72 /* Context activation is done by the caller. */
73 static void wined3d_volume_allocate_texture(struct wined3d_volume *volume,
74 const struct wined3d_context *context, BOOL srgb)
76 const struct wined3d_gl_info *gl_info = context->gl_info;
77 const struct wined3d_format *format = volume->resource.format;
78 void *mem = NULL;
80 if (gl_info->supported[APPLE_CLIENT_STORAGE] && !format->convert
81 && volume_prepare_system_memory(volume))
83 TRACE("Enabling GL_UNPACK_CLIENT_STORAGE_APPLE for volume %p\n", volume);
84 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
85 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
86 mem = volume->resource.heap_memory;
87 volume->flags |= WINED3D_VFLAG_CLIENT_STORAGE;
90 GL_EXTCALL(glTexImage3DEXT(GL_TEXTURE_3D, volume->texture_level,
91 srgb ? format->glGammaInternal : format->glInternal,
92 volume->resource.width, volume->resource.height, volume->resource.depth,
93 0, format->glFormat, format->glType, mem));
94 checkGLcall("glTexImage3D");
96 if (mem)
98 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
99 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
103 static void wined3d_volume_get_pitch(const struct wined3d_volume *volume, UINT *row_pitch,
104 UINT *slice_pitch)
106 const struct wined3d_format *format = volume->resource.format;
108 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
110 /* Since compressed formats are block based, pitch means the amount of
111 * bytes to the next row of block rather than the next row of pixels. */
112 UINT row_block_count = (volume->resource.width + format->block_width - 1) / format->block_width;
113 UINT slice_block_count = (volume->resource.height + format->block_height - 1) / format->block_height;
114 *row_pitch = row_block_count * format->block_byte_count;
115 *slice_pitch = *row_pitch * slice_block_count;
117 else
119 unsigned char alignment = volume->resource.device->surface_alignment;
120 *row_pitch = format->byte_count * volume->resource.width; /* Bytes / row */
121 *row_pitch = (*row_pitch + alignment - 1) & ~(alignment - 1);
122 *slice_pitch = *row_pitch * volume->resource.height;
125 TRACE("Returning row pitch %u, slice pitch %u.\n", *row_pitch, *slice_pitch);
128 /* Context activation is done by the caller. */
129 void wined3d_volume_upload_data(struct wined3d_volume *volume, const struct wined3d_context *context,
130 const struct wined3d_bo_address *data)
132 const struct wined3d_gl_info *gl_info = context->gl_info;
133 const struct wined3d_format *format = volume->resource.format;
134 UINT width = volume->resource.width;
135 UINT height = volume->resource.height;
136 UINT depth = volume->resource.depth;
137 BYTE *mem = data->addr;
139 TRACE("volume %p, context %p, level %u, format %s (%#x).\n",
140 volume, context, volume->texture_level, debug_d3dformat(format->id),
141 format->id);
143 if (format->convert)
145 UINT dst_row_pitch, dst_slice_pitch;
146 UINT src_row_pitch, src_slice_pitch;
147 UINT alignment = volume->resource.device->surface_alignment;
149 if (data->buffer_object)
150 ERR("Loading a converted volume from a PBO.\n");
151 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
152 ERR("Converting a block-based format.\n");
154 dst_row_pitch = width * format->conv_byte_count;
155 dst_row_pitch = (dst_row_pitch + alignment - 1) & ~(alignment - 1);
156 dst_slice_pitch = dst_row_pitch * height;
158 wined3d_volume_get_pitch(volume, &src_row_pitch, &src_slice_pitch);
160 mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch * depth);
161 format->convert(data->addr, mem, src_row_pitch, src_slice_pitch,
162 dst_row_pitch, dst_slice_pitch, width, height, depth);
165 if (data->buffer_object)
167 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
168 checkGLcall("glBindBufferARB");
171 GL_EXTCALL(glTexSubImage3DEXT(GL_TEXTURE_3D, volume->texture_level, 0, 0, 0,
172 width, height, depth,
173 format->glFormat, format->glType, mem));
174 checkGLcall("glTexSubImage3D");
176 if (data->buffer_object)
178 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
179 checkGLcall("glBindBufferARB");
182 if (mem != data->addr)
183 HeapFree(GetProcessHeap(), 0, mem);
186 static void wined3d_volume_validate_location(struct wined3d_volume *volume, DWORD location)
188 TRACE("Volume %p, setting %s.\n", volume, wined3d_debug_location(location));
189 volume->locations |= location;
190 TRACE("new location flags are %s.\n", wined3d_debug_location(volume->locations));
193 void wined3d_volume_invalidate_location(struct wined3d_volume *volume, DWORD location)
195 TRACE("Volume %p, clearing %s.\n", volume, wined3d_debug_location(location));
196 volume->locations &= ~location;
197 TRACE("new location flags are %s.\n", wined3d_debug_location(volume->locations));
200 /* Context activation is done by the caller. */
201 static void wined3d_volume_download_data(struct wined3d_volume *volume,
202 const struct wined3d_context *context, const struct wined3d_bo_address *data)
204 const struct wined3d_gl_info *gl_info = context->gl_info;
205 const struct wined3d_format *format = volume->resource.format;
207 if (format->convert)
209 FIXME("Attempting to download a converted volume, format %s.\n",
210 debug_d3dformat(format->id));
211 return;
214 if (data->buffer_object)
216 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data->buffer_object));
217 checkGLcall("glBindBufferARB");
220 gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_3D, volume->texture_level,
221 format->glFormat, format->glType, data->addr);
222 checkGLcall("glGetTexImage");
224 if (data->buffer_object)
226 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
227 checkGLcall("glBindBufferARB");
232 static void wined3d_volume_evict_sysmem(struct wined3d_volume *volume)
234 wined3d_resource_free_sysmem(&volume->resource);
235 wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_SYSMEM);
238 static DWORD volume_access_from_location(DWORD location)
240 switch (location)
242 case WINED3D_LOCATION_DISCARDED:
243 return 0;
245 case WINED3D_LOCATION_SYSMEM:
246 return WINED3D_RESOURCE_ACCESS_CPU;
248 case WINED3D_LOCATION_BUFFER:
249 case WINED3D_LOCATION_TEXTURE_RGB:
250 case WINED3D_LOCATION_TEXTURE_SRGB:
251 return WINED3D_RESOURCE_ACCESS_GPU;
253 default:
254 FIXME("Unhandled location %#x.\n", location);
255 return 0;
259 /* Context activation is done by the caller. */
260 static void wined3d_volume_srgb_transfer(struct wined3d_volume *volume,
261 struct wined3d_context *context, BOOL dest_is_srgb)
263 struct wined3d_bo_address data;
264 /* Optimizations are possible, but the effort should be put into either
265 * implementing EXT_SRGB_DECODE in the driver or finding out why we
266 * picked the wrong copy for the original upload and fixing that.
268 * Also keep in mind that we want to avoid using resource.heap_memory
269 * for DEFAULT pool surfaces. */
271 WARN_(d3d_perf)("Performing slow rgb/srgb volume transfer.\n");
272 data.buffer_object = 0;
273 data.addr = HeapAlloc(GetProcessHeap(), 0, volume->resource.size);
274 if (!data.addr)
275 return;
277 volume_bind_and_dirtify(volume, context, !dest_is_srgb);
278 wined3d_volume_download_data(volume, context, &data);
279 volume_bind_and_dirtify(volume, context, dest_is_srgb);
280 wined3d_volume_upload_data(volume, context, &data);
282 HeapFree(GetProcessHeap(), 0, data.addr);
285 static BOOL wined3d_volume_can_evict(const struct wined3d_volume *volume)
287 if (volume->resource.pool != WINED3D_POOL_MANAGED)
288 return FALSE;
289 if (volume->download_count >= 10)
290 return FALSE;
291 if (volume->resource.format->convert)
292 return FALSE;
293 if (volume->flags & WINED3D_VFLAG_CLIENT_STORAGE)
294 return FALSE;
296 return TRUE;
298 /* Context activation is done by the caller. */
299 static void wined3d_volume_load_location(struct wined3d_volume *volume,
300 struct wined3d_context *context, DWORD location)
302 DWORD required_access = volume_access_from_location(location);
304 TRACE("Volume %p, loading %s, have %s.\n", volume, wined3d_debug_location(location),
305 wined3d_debug_location(volume->locations));
307 if ((volume->locations & location) == location)
309 TRACE("Location(s) already up to date.\n");
310 return;
313 if ((volume->resource.access_flags & required_access) != required_access)
315 ERR("Operation requires %#x access, but volume only has %#x.\n",
316 required_access, volume->resource.access_flags);
317 return;
320 switch (location)
322 case WINED3D_LOCATION_TEXTURE_RGB:
323 case WINED3D_LOCATION_TEXTURE_SRGB:
324 if ((location == WINED3D_LOCATION_TEXTURE_RGB
325 && !(volume->flags & WINED3D_VFLAG_ALLOCATED))
326 || (location == WINED3D_LOCATION_TEXTURE_SRGB
327 && !(volume->flags & WINED3D_VFLAG_SRGB_ALLOCATED)))
328 ERR("Trying to load (s)RGB texture without prior allocation.\n");
330 if (volume->locations & WINED3D_LOCATION_DISCARDED)
332 TRACE("Volume previously discarded, nothing to do.\n");
333 wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
335 else if (volume->locations & WINED3D_LOCATION_SYSMEM)
337 struct wined3d_bo_address data = {0, volume->resource.heap_memory};
338 wined3d_volume_upload_data(volume, context, &data);
340 else if (volume->locations & WINED3D_LOCATION_BUFFER)
342 struct wined3d_bo_address data = {volume->pbo, NULL};
343 wined3d_volume_upload_data(volume, context, &data);
345 else if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
347 wined3d_volume_srgb_transfer(volume, context, TRUE);
349 else if (volume->locations & WINED3D_LOCATION_TEXTURE_SRGB)
351 wined3d_volume_srgb_transfer(volume, context, FALSE);
353 else
355 FIXME("Implement texture loading from %s.\n", wined3d_debug_location(volume->locations));
356 return;
358 wined3d_volume_validate_location(volume, location);
360 if (wined3d_volume_can_evict(volume))
361 wined3d_volume_evict_sysmem(volume);
363 break;
365 case WINED3D_LOCATION_SYSMEM:
366 if (!volume->resource.heap_memory)
367 ERR("Trying to load WINED3D_LOCATION_SYSMEM without setting it up first.\n");
369 if (volume->locations & WINED3D_LOCATION_DISCARDED)
371 TRACE("Volume previously discarded, nothing to do.\n");
372 wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
374 else if (volume->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
376 struct wined3d_bo_address data = {0, volume->resource.heap_memory};
378 if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
379 volume_bind_and_dirtify(volume, context, FALSE);
380 else
381 volume_bind_and_dirtify(volume, context, TRUE);
383 volume->download_count++;
384 wined3d_volume_download_data(volume, context, &data);
386 else
388 FIXME("Implement WINED3D_LOCATION_SYSMEM loading from %s.\n",
389 wined3d_debug_location(volume->locations));
390 return;
392 wined3d_volume_validate_location(volume, WINED3D_LOCATION_SYSMEM);
393 break;
395 case WINED3D_LOCATION_BUFFER:
396 if (!volume->pbo || !(volume->flags & WINED3D_VFLAG_PBO))
397 ERR("Trying to load WINED3D_LOCATION_BUFFER without setting it up first.\n");
399 if (volume->locations & WINED3D_LOCATION_DISCARDED)
401 TRACE("Volume previously discarded, nothing to do.\n");
402 wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
404 else if (volume->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
406 struct wined3d_bo_address data = {volume->pbo, NULL};
408 if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
409 volume_bind_and_dirtify(volume, context, FALSE);
410 else
411 volume_bind_and_dirtify(volume, context, TRUE);
413 wined3d_volume_download_data(volume, context, &data);
415 else
417 FIXME("Implement WINED3D_LOCATION_BUFFER loading from %s.\n",
418 wined3d_debug_location(volume->locations));
419 return;
421 wined3d_volume_validate_location(volume, WINED3D_LOCATION_BUFFER);
422 break;
424 default:
425 FIXME("Implement %s loading from %s.\n", wined3d_debug_location(location),
426 wined3d_debug_location(volume->locations));
430 /* Context activation is done by the caller. */
431 void wined3d_volume_load(struct wined3d_volume *volume, struct wined3d_context *context, BOOL srgb_mode)
433 volume_bind_and_dirtify(volume, context, srgb_mode);
435 if (srgb_mode)
437 if (!(volume->flags & WINED3D_VFLAG_SRGB_ALLOCATED))
439 wined3d_volume_allocate_texture(volume, context, TRUE);
440 volume->flags |= WINED3D_VFLAG_SRGB_ALLOCATED;
443 wined3d_volume_load_location(volume, context, WINED3D_LOCATION_TEXTURE_SRGB);
445 else
447 if (!(volume->flags & WINED3D_VFLAG_ALLOCATED))
449 wined3d_volume_allocate_texture(volume, context, FALSE);
450 volume->flags |= WINED3D_VFLAG_ALLOCATED;
453 wined3d_volume_load_location(volume, context, WINED3D_LOCATION_TEXTURE_RGB);
457 /* Context activation is done by the caller. */
458 static void wined3d_volume_prepare_pbo(struct wined3d_volume *volume, struct wined3d_context *context)
460 const struct wined3d_gl_info *gl_info = context->gl_info;
462 if (volume->pbo)
463 return;
465 GL_EXTCALL(glGenBuffersARB(1, &volume->pbo));
466 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, volume->pbo));
467 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, volume->resource.size, NULL, GL_STREAM_DRAW_ARB));
468 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
469 checkGLcall("Create PBO");
471 TRACE("Created PBO %u for volume %p.\n", volume->pbo, volume);
474 static void wined3d_volume_free_pbo(struct wined3d_volume *volume)
476 struct wined3d_context *context = context_acquire(volume->resource.device, NULL);
477 const struct wined3d_gl_info *gl_info = context->gl_info;
479 TRACE("Deleting PBO %u belonging to volume %p.\n", volume->pbo, volume);
480 GL_EXTCALL(glDeleteBuffersARB(1, &volume->pbo));
481 checkGLcall("glDeleteBuffersARB");
482 volume->pbo = 0;
483 context_release(context);
486 static void volume_unload(struct wined3d_resource *resource)
488 struct wined3d_volume *volume = volume_from_resource(resource);
489 struct wined3d_device *device = volume->resource.device;
490 struct wined3d_context *context;
492 if (volume->resource.pool == WINED3D_POOL_DEFAULT)
493 ERR("Unloading DEFAULT pool volume.\n");
495 TRACE("texture %p.\n", resource);
497 if (volume_prepare_system_memory(volume))
499 context = context_acquire(device, NULL);
500 wined3d_volume_load_location(volume, context, WINED3D_LOCATION_SYSMEM);
501 context_release(context);
502 wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_SYSMEM);
504 else
506 ERR("Out of memory when unloading volume %p.\n", volume);
507 wined3d_volume_validate_location(volume, WINED3D_LOCATION_DISCARDED);
508 wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_DISCARDED);
511 if (volume->pbo)
513 /* Should not happen because only dynamic default pool volumes
514 * have a buffer, and those are not evicted by device_evit_managed_resources
515 * and must be freed before a non-ex device reset. */
516 ERR("Unloading a volume with a buffer\n");
517 wined3d_volume_free_pbo(volume);
520 /* The texture name is managed by the container. */
521 volume->flags &= ~(WINED3D_VFLAG_ALLOCATED | WINED3D_VFLAG_SRGB_ALLOCATED
522 | WINED3D_VFLAG_CLIENT_STORAGE);
524 resource_unload(resource);
527 ULONG CDECL wined3d_volume_incref(struct wined3d_volume *volume)
529 ULONG refcount;
531 if (volume->container)
533 TRACE("Forwarding to container %p.\n", volume->container);
534 return wined3d_texture_incref(volume->container);
537 refcount = InterlockedIncrement(&volume->resource.ref);
539 TRACE("%p increasing refcount to %u.\n", volume, refcount);
541 return refcount;
544 ULONG CDECL wined3d_volume_decref(struct wined3d_volume *volume)
546 ULONG refcount;
548 if (volume->container)
550 TRACE("Forwarding to container %p.\n", volume->container);
551 return wined3d_texture_decref(volume->container);
554 refcount = InterlockedDecrement(&volume->resource.ref);
556 TRACE("%p decreasing refcount to %u.\n", volume, refcount);
558 if (!refcount)
560 if (volume->pbo)
561 wined3d_volume_free_pbo(volume);
563 resource_cleanup(&volume->resource);
564 volume->resource.parent_ops->wined3d_object_destroyed(volume->resource.parent);
565 HeapFree(GetProcessHeap(), 0, volume);
568 return refcount;
571 void * CDECL wined3d_volume_get_parent(const struct wined3d_volume *volume)
573 TRACE("volume %p.\n", volume);
575 return volume->resource.parent;
578 DWORD CDECL wined3d_volume_set_priority(struct wined3d_volume *volume, DWORD priority)
580 return resource_set_priority(&volume->resource, priority);
583 DWORD CDECL wined3d_volume_get_priority(const struct wined3d_volume *volume)
585 return resource_get_priority(&volume->resource);
588 void CDECL wined3d_volume_preload(struct wined3d_volume *volume)
590 FIXME("volume %p stub!\n", volume);
593 struct wined3d_resource * CDECL wined3d_volume_get_resource(struct wined3d_volume *volume)
595 TRACE("volume %p.\n", volume);
597 return &volume->resource;
600 static BOOL volume_check_block_align(const struct wined3d_volume *volume,
601 const struct wined3d_box *box)
603 UINT width_mask, height_mask;
604 const struct wined3d_format *format = volume->resource.format;
606 if (!box)
607 return TRUE;
609 /* This assumes power of two block sizes, but NPOT block sizes would be
610 * silly anyway.
612 * This also assumes that the format's block depth is 1. */
613 width_mask = format->block_width - 1;
614 height_mask = format->block_height - 1;
616 if (box->left & width_mask)
617 return FALSE;
618 if (box->top & height_mask)
619 return FALSE;
620 if (box->right & width_mask && box->right != volume->resource.width)
621 return FALSE;
622 if (box->bottom & height_mask && box->bottom != volume->resource.height)
623 return FALSE;
625 return TRUE;
628 static BOOL wined3d_volume_check_box_dimensions(const struct wined3d_volume *volume,
629 const struct wined3d_box *box)
631 if (!box)
632 return TRUE;
634 if (box->left >= box->right)
635 return FALSE;
636 if (box->top >= box->bottom)
637 return FALSE;
638 if (box->front >= box->back)
639 return FALSE;
640 if (box->right > volume->resource.width)
641 return FALSE;
642 if (box->bottom > volume->resource.height)
643 return FALSE;
644 if (box->back > volume->resource.depth)
645 return FALSE;
647 return TRUE;
650 HRESULT CDECL wined3d_volume_map(struct wined3d_volume *volume,
651 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
653 struct wined3d_device *device = volume->resource.device;
654 struct wined3d_context *context;
655 const struct wined3d_gl_info *gl_info;
656 BYTE *base_memory;
657 const struct wined3d_format *format = volume->resource.format;
659 TRACE("volume %p, map_desc %p, box %p, flags %#x.\n",
660 volume, map_desc, box, flags);
662 map_desc->data = NULL;
663 if (!(volume->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
665 WARN("Volume %p is not CPU accessible.\n", volume);
666 return WINED3DERR_INVALIDCALL;
668 if (volume->resource.map_count)
670 WARN("Volume is already mapped.\n");
671 return WINED3DERR_INVALIDCALL;
673 if (!wined3d_volume_check_box_dimensions(volume, box))
675 WARN("Map box is invalid.\n");
676 return WINED3DERR_INVALIDCALL;
678 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && !volume_check_block_align(volume, box))
680 WARN("Map box is misaligned for %ux%u blocks.\n",
681 format->block_width, format->block_height);
682 return WINED3DERR_INVALIDCALL;
685 flags = wined3d_resource_sanitize_map_flags(&volume->resource, flags);
687 if (volume->flags & WINED3D_VFLAG_PBO)
689 context = context_acquire(device, NULL);
690 gl_info = context->gl_info;
692 wined3d_volume_prepare_pbo(volume, context);
693 if (flags & WINED3D_MAP_DISCARD)
694 wined3d_volume_validate_location(volume, WINED3D_LOCATION_BUFFER);
695 else
696 wined3d_volume_load_location(volume, context, WINED3D_LOCATION_BUFFER);
698 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, volume->pbo));
700 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
702 GLbitfield mapflags = wined3d_resource_gl_map_flags(flags);
703 mapflags &= ~GL_MAP_FLUSH_EXPLICIT_BIT;
704 base_memory = GL_EXTCALL(glMapBufferRange(GL_PIXEL_UNPACK_BUFFER_ARB,
705 0, volume->resource.size, mapflags));
707 else
709 GLenum access = wined3d_resource_gl_legacy_map_flags(flags);
710 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, access));
713 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
714 checkGLcall("Map PBO");
716 context_release(context);
718 else
720 if (!volume_prepare_system_memory(volume))
722 WARN("Out of memory.\n");
723 map_desc->data = NULL;
724 return E_OUTOFMEMORY;
727 if (flags & WINED3D_MAP_DISCARD)
729 wined3d_volume_validate_location(volume, WINED3D_LOCATION_SYSMEM);
731 else if (!(volume->locations & WINED3D_LOCATION_SYSMEM))
733 context = context_acquire(device, NULL);
734 wined3d_volume_load_location(volume, context, WINED3D_LOCATION_SYSMEM);
735 context_release(context);
737 base_memory = volume->resource.heap_memory;
740 TRACE("Base memory pointer %p.\n", base_memory);
742 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
744 map_desc->row_pitch = volume->resource.width * format->byte_count;
745 map_desc->slice_pitch = map_desc->row_pitch * volume->resource.height;
747 else
749 wined3d_volume_get_pitch(volume, &map_desc->row_pitch, &map_desc->slice_pitch);
752 if (!box)
754 TRACE("No box supplied - all is ok\n");
755 map_desc->data = base_memory;
757 else
759 TRACE("Lock Box (%p) = l %u, t %u, r %u, b %u, fr %u, ba %u\n",
760 box, box->left, box->top, box->right, box->bottom, box->front, box->back);
762 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
764 /* Compressed textures are block based, so calculate the offset of
765 * the block that contains the top-left pixel of the locked rectangle. */
766 map_desc->data = base_memory
767 + (box->front * map_desc->slice_pitch)
768 + ((box->top / format->block_height) * map_desc->row_pitch)
769 + ((box->left / format->block_width) * format->block_byte_count);
771 else
773 map_desc->data = base_memory
774 + (map_desc->slice_pitch * box->front)
775 + (map_desc->row_pitch * box->top)
776 + (box->left * volume->resource.format->byte_count);
780 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
782 wined3d_texture_set_dirty(volume->container);
784 if (volume->flags & WINED3D_VFLAG_PBO)
785 wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_BUFFER);
786 else
787 wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_SYSMEM);
790 volume->resource.map_count++;
792 TRACE("Returning memory %p, row pitch %d, slice pitch %d.\n",
793 map_desc->data, map_desc->row_pitch, map_desc->slice_pitch);
795 return WINED3D_OK;
798 struct wined3d_volume * CDECL wined3d_volume_from_resource(struct wined3d_resource *resource)
800 return volume_from_resource(resource);
803 HRESULT CDECL wined3d_volume_unmap(struct wined3d_volume *volume)
805 TRACE("volume %p.\n", volume);
807 if (!volume->resource.map_count)
809 WARN("Trying to unlock an unlocked volume %p.\n", volume);
810 return WINED3DERR_INVALIDCALL;
813 if (volume->flags & WINED3D_VFLAG_PBO)
815 struct wined3d_device *device = volume->resource.device;
816 struct wined3d_context *context = context_acquire(device, NULL);
817 const struct wined3d_gl_info *gl_info = context->gl_info;
819 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, volume->pbo));
820 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
821 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
822 checkGLcall("Unmap PBO");
824 context_release(context);
827 volume->resource.map_count--;
829 return WINED3D_OK;
832 static const struct wined3d_resource_ops volume_resource_ops =
834 volume_unload,
837 static HRESULT volume_init(struct wined3d_volume *volume, struct wined3d_device *device, UINT width,
838 UINT height, UINT depth, UINT level, DWORD usage, enum wined3d_format_id format_id,
839 enum wined3d_pool pool, void *parent, const struct wined3d_parent_ops *parent_ops)
841 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
842 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
843 HRESULT hr;
844 UINT size;
846 if (!gl_info->supported[EXT_TEXTURE3D])
848 WARN("Volume cannot be created - no volume texture support.\n");
849 return WINED3DERR_INVALIDCALL;
851 /* TODO: Write tests for other resources and move this check
852 * to resource_init, if applicable. */
853 if (usage & WINED3DUSAGE_DYNAMIC
854 && (pool == WINED3D_POOL_MANAGED || pool == WINED3D_POOL_SCRATCH))
856 WARN("Attempted to create a DYNAMIC texture in pool %u.\n", pool);
857 return WINED3DERR_INVALIDCALL;
860 size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, depth);
862 hr = resource_init(&volume->resource, device, WINED3D_RTYPE_VOLUME, format,
863 WINED3D_MULTISAMPLE_NONE, 0, usage, pool, width, height, depth,
864 size, parent, parent_ops, &volume_resource_ops);
865 if (FAILED(hr))
867 WARN("Failed to initialize resource, returning %#x.\n", hr);
868 return hr;
871 volume->texture_level = level;
872 volume->locations = WINED3D_LOCATION_DISCARDED;
874 if (pool == WINED3D_POOL_DEFAULT && usage & WINED3DUSAGE_DYNAMIC
875 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
876 && !format->convert)
878 wined3d_resource_free_sysmem(&volume->resource);
879 volume->flags |= WINED3D_VFLAG_PBO;
882 return WINED3D_OK;
885 HRESULT CDECL wined3d_volume_create(struct wined3d_device *device, UINT width, UINT height,
886 UINT depth, UINT level, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool,
887 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_volume **volume)
889 struct wined3d_volume *object;
890 HRESULT hr;
892 TRACE("device %p, width %u, height %u, depth %u, usage %#x, format %s, pool %s\n",
893 device, width, height, depth, usage, debug_d3dformat(format_id), debug_d3dpool(pool));
894 TRACE("parent %p, parent_ops %p, volume %p.\n", parent, parent_ops, volume);
896 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
897 if (!object)
899 *volume = NULL;
900 return WINED3DERR_OUTOFVIDEOMEMORY;
903 hr = volume_init(object, device, width, height, depth, level,
904 usage, format_id, pool, parent, parent_ops);
905 if (FAILED(hr))
907 WARN("Failed to initialize volume, returning %#x.\n", hr);
908 HeapFree(GetProcessHeap(), 0, object);
909 return hr;
912 TRACE("Created volume %p.\n", object);
913 *volume = object;
915 return WINED3D_OK;