volume.c 28.6 KB
Newer Older
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
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
21 22 23
 */

#include "config.h"
24
#include "wine/port.h"
25 26
#include "wined3d_private.h"

27
WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
28
WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
29

30
BOOL volume_prepare_system_memory(struct wined3d_volume *volume)
31 32 33 34 35 36 37 38 39 40 41 42
{
    if (volume->resource.heap_memory)
        return TRUE;

    if (!wined3d_resource_allocate_sysmem(&volume->resource))
    {
        ERR("Failed to allocate system memory.\n");
        return FALSE;
    }
    return TRUE;
}

43
void wined3d_volume_get_pitch(const struct wined3d_volume *volume, UINT *row_pitch, UINT *slice_pitch)
44 45 46
{
    const struct wined3d_format *format = volume->resource.format;

47
    if (volume->container->resource.format_flags & WINED3DFMT_FLAG_BLOCKS)
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    {
        /* Since compressed formats are block based, pitch means the amount of
         * bytes to the next row of block rather than the next row of pixels. */
        UINT row_block_count = (volume->resource.width + format->block_width - 1) / format->block_width;
        UINT slice_block_count = (volume->resource.height + format->block_height - 1) / format->block_height;
        *row_pitch = row_block_count * format->block_byte_count;
        *slice_pitch = *row_pitch * slice_block_count;
    }
    else
    {
        unsigned char alignment = volume->resource.device->surface_alignment;
        *row_pitch = format->byte_count * volume->resource.width;  /* Bytes / row */
        *row_pitch = (*row_pitch + alignment - 1) & ~(alignment - 1);
        *slice_pitch = *row_pitch * volume->resource.height;
    }

    TRACE("Returning row pitch %u, slice pitch %u.\n", *row_pitch, *slice_pitch);
}

67 68
/* This call just uploads data, the caller is responsible for binding the
 * correct texture. */
69
/* Context activation is done by the caller. */
70
void wined3d_volume_upload_data(struct wined3d_volume *volume, const struct wined3d_context *context,
71
        const struct wined3d_const_bo_address *data)
72
{
73
    const struct wined3d_gl_info *gl_info = context->gl_info;
74
    const struct wined3d_format *format = volume->resource.format;
75 76
    UINT width = volume->resource.width;
    UINT height = volume->resource.height;
77
    UINT depth = volume->resource.depth;
78 79
    const void *mem = data->addr;
    void *converted_mem = NULL;
80

81 82
    TRACE("volume %p, context %p, level %u, format %s (%#x).\n",
            volume, context, volume->texture_level, debug_d3dformat(format->id),
83
            format->id);
84

85 86 87 88 89 90 91
    if (format->convert)
    {
        UINT dst_row_pitch, dst_slice_pitch;
        UINT src_row_pitch, src_slice_pitch;

        if (data->buffer_object)
            ERR("Loading a converted volume from a PBO.\n");
92
        if (volume->container->resource.format_flags & WINED3DFMT_FLAG_BLOCKS)
93 94 95 96 97 98 99
            ERR("Converting a block-based format.\n");

        dst_row_pitch = width * format->conv_byte_count;
        dst_slice_pitch = dst_row_pitch * height;

        wined3d_volume_get_pitch(volume, &src_row_pitch, &src_slice_pitch);

100 101
        converted_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch * depth);
        format->convert(data->addr, converted_mem, src_row_pitch, src_slice_pitch,
102
                dst_row_pitch, dst_slice_pitch, width, height, depth);
103
        mem = converted_mem;
104
    }
105 106 107

    if (data->buffer_object)
    {
108 109
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
        checkGLcall("glBindBuffer");
110 111
    }

112
    GL_EXTCALL(glTexSubImage3D(GL_TEXTURE_3D, volume->texture_level, 0, 0, 0,
113 114
            width, height, depth,
            format->glFormat, format->glType, mem));
115 116 117 118
    checkGLcall("glTexSubImage3D");

    if (data->buffer_object)
    {
119 120
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
        checkGLcall("glBindBuffer");
121
    }
122

123
    HeapFree(GetProcessHeap(), 0, converted_mem);
124 125
}

126
void wined3d_volume_validate_location(struct wined3d_volume *volume, DWORD location)
127 128 129 130 131 132
{
    TRACE("Volume %p, setting %s.\n", volume, wined3d_debug_location(location));
    volume->locations |= location;
    TRACE("new location flags are %s.\n", wined3d_debug_location(volume->locations));
}

133
void wined3d_volume_invalidate_location(struct wined3d_volume *volume, DWORD location)
134 135 136 137 138 139
{
    TRACE("Volume %p, clearing %s.\n", volume, wined3d_debug_location(location));
    volume->locations &= ~location;
    TRACE("new location flags are %s.\n", wined3d_debug_location(volume->locations));
}

140 141
/* Context activation is done by the caller. */
static void wined3d_volume_download_data(struct wined3d_volume *volume,
142
        const struct wined3d_context *context, const struct wined3d_bo_address *data)
143 144 145 146
{
    const struct wined3d_gl_info *gl_info = context->gl_info;
    const struct wined3d_format *format = volume->resource.format;

147 148 149 150 151 152 153
    if (format->convert)
    {
        FIXME("Attempting to download a converted volume, format %s.\n",
                debug_d3dformat(format->id));
        return;
    }

154 155
    if (data->buffer_object)
    {
156 157
        GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data->buffer_object));
        checkGLcall("glBindBuffer");
158 159
    }

160
    gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_3D, volume->texture_level,
161
            format->glFormat, format->glType, data->addr);
162
    checkGLcall("glGetTexImage");
163 164 165

    if (data->buffer_object)
    {
166 167
        GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
        checkGLcall("glBindBuffer");
168 169
    }

170 171 172 173
}

static void wined3d_volume_evict_sysmem(struct wined3d_volume *volume)
{
174
    wined3d_resource_free_sysmem(&volume->resource);
175 176 177
    wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_SYSMEM);
}

178 179 180 181 182 183 184 185 186 187
static DWORD volume_access_from_location(DWORD location)
{
    switch (location)
    {
        case WINED3D_LOCATION_DISCARDED:
            return 0;

        case WINED3D_LOCATION_SYSMEM:
            return WINED3D_RESOURCE_ACCESS_CPU;

188
        case WINED3D_LOCATION_BUFFER:
189
        case WINED3D_LOCATION_TEXTURE_RGB:
190
        case WINED3D_LOCATION_TEXTURE_SRGB:
191 192 193 194 195 196 197 198
            return WINED3D_RESOURCE_ACCESS_GPU;

        default:
            FIXME("Unhandled location %#x.\n", location);
            return 0;
    }
}

199 200 201 202 203 204 205 206 207
/* Context activation is done by the caller. */
static void wined3d_volume_srgb_transfer(struct wined3d_volume *volume,
        struct wined3d_context *context, BOOL dest_is_srgb)
{
    struct wined3d_bo_address data;
    /* Optimizations are possible, but the effort should be put into either
     * implementing EXT_SRGB_DECODE in the driver or finding out why we
     * picked the wrong copy for the original upload and fixing that.
     *
208
     * Also keep in mind that we want to avoid using resource.heap_memory
209 210 211 212 213 214 215 216
     * for DEFAULT pool surfaces. */

    WARN_(d3d_perf)("Performing slow rgb/srgb volume transfer.\n");
    data.buffer_object = 0;
    data.addr = HeapAlloc(GetProcessHeap(), 0, volume->resource.size);
    if (!data.addr)
        return;

217
    wined3d_texture_bind_and_dirtify(volume->container, context, !dest_is_srgb);
218
    wined3d_volume_download_data(volume, context, &data);
219
    wined3d_texture_bind_and_dirtify(volume->container, context, dest_is_srgb);
220
    wined3d_volume_upload_data(volume, context, wined3d_const_bo_address(&data));
221 222 223 224

    HeapFree(GetProcessHeap(), 0, data.addr);
}

225 226 227 228 229 230 231 232
static BOOL wined3d_volume_can_evict(const struct wined3d_volume *volume)
{
    if (volume->resource.pool != WINED3D_POOL_MANAGED)
        return FALSE;
    if (volume->download_count >= 10)
        return FALSE;
    if (volume->resource.format->convert)
        return FALSE;
233 234
    if (volume->flags & WINED3D_VFLAG_CLIENT_STORAGE)
        return FALSE;
235 236 237

    return TRUE;
}
238 239 240 241
/* Context activation is done by the caller. */
static void wined3d_volume_load_location(struct wined3d_volume *volume,
        struct wined3d_context *context, DWORD location)
{
242 243
    DWORD required_access = volume_access_from_location(location);

244 245 246 247 248 249 250 251 252
    TRACE("Volume %p, loading %s, have %s.\n", volume, wined3d_debug_location(location),
        wined3d_debug_location(volume->locations));

    if ((volume->locations & location) == location)
    {
        TRACE("Location(s) already up to date.\n");
        return;
    }

253
    if ((volume->resource.access_flags & required_access) != required_access)
254
    {
255 256 257
        ERR("Operation requires %#x access, but volume only has %#x.\n",
                required_access, volume->resource.access_flags);
        return;
258 259 260 261 262
    }

    switch (location)
    {
        case WINED3D_LOCATION_TEXTURE_RGB:
263 264
        case WINED3D_LOCATION_TEXTURE_SRGB:
            if ((location == WINED3D_LOCATION_TEXTURE_RGB
265
                    && !(volume->container->flags & WINED3D_TEXTURE_RGB_ALLOCATED))
266
                    || (location == WINED3D_LOCATION_TEXTURE_SRGB
267
                    && !(volume->container->flags & WINED3D_TEXTURE_SRGB_ALLOCATED)))
268
                ERR("Trying to load (s)RGB texture without prior allocation.\n");
269 270 271 272 273 274 275 276

            if (volume->locations & WINED3D_LOCATION_DISCARDED)
            {
                TRACE("Volume previously discarded, nothing to do.\n");
                wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
            }
            else if (volume->locations & WINED3D_LOCATION_SYSMEM)
            {
277
                struct wined3d_const_bo_address data = {0, volume->resource.heap_memory};
278 279
                wined3d_texture_bind_and_dirtify(volume->container, context,
                        location == WINED3D_LOCATION_TEXTURE_SRGB);
280 281
                wined3d_volume_upload_data(volume, context, &data);
            }
282 283
            else if (volume->locations & WINED3D_LOCATION_BUFFER)
            {
284
                struct wined3d_const_bo_address data = {volume->pbo, NULL};
285 286
                wined3d_texture_bind_and_dirtify(volume->container, context,
                        location == WINED3D_LOCATION_TEXTURE_SRGB);
287 288
                wined3d_volume_upload_data(volume, context, &data);
            }
289 290 291 292 293 294 295 296
            else if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
            {
                wined3d_volume_srgb_transfer(volume, context, TRUE);
            }
            else if (volume->locations & WINED3D_LOCATION_TEXTURE_SRGB)
            {
                wined3d_volume_srgb_transfer(volume, context, FALSE);
            }
297 298 299 300 301
            else
            {
                FIXME("Implement texture loading from %s.\n", wined3d_debug_location(volume->locations));
                return;
            }
302
            wined3d_volume_validate_location(volume, location);
303

304
            if (wined3d_volume_can_evict(volume))
305 306
                wined3d_volume_evict_sysmem(volume);

307 308 309
            break;

        case WINED3D_LOCATION_SYSMEM:
310
            if (!volume->resource.heap_memory)
311 312 313 314 315 316 317
                ERR("Trying to load WINED3D_LOCATION_SYSMEM without setting it up first.\n");

            if (volume->locations & WINED3D_LOCATION_DISCARDED)
            {
                TRACE("Volume previously discarded, nothing to do.\n");
                wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
            }
318
            else if (volume->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
319
            {
320
                struct wined3d_bo_address data = {0, volume->resource.heap_memory};
321 322

                if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
323
                    wined3d_texture_bind_and_dirtify(volume->container, context, FALSE);
324
                else
325
                    wined3d_texture_bind_and_dirtify(volume->container, context, TRUE);
326

327
                volume->download_count++;
328
                wined3d_volume_download_data(volume, context, &data);
329
            }
330 331 332 333 334 335 336 337 338
            else
            {
                FIXME("Implement WINED3D_LOCATION_SYSMEM loading from %s.\n",
                        wined3d_debug_location(volume->locations));
                return;
            }
            wined3d_volume_validate_location(volume, WINED3D_LOCATION_SYSMEM);
            break;

339
        case WINED3D_LOCATION_BUFFER:
340
            if (!volume->pbo)
341 342 343 344 345 346 347
                ERR("Trying to load WINED3D_LOCATION_BUFFER without setting it up first.\n");

            if (volume->locations & WINED3D_LOCATION_DISCARDED)
            {
                TRACE("Volume previously discarded, nothing to do.\n");
                wined3d_volume_invalidate_location(volume, WINED3D_LOCATION_DISCARDED);
            }
348
            else if (volume->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
349 350
            {
                struct wined3d_bo_address data = {volume->pbo, NULL};
351 352

                if (volume->locations & WINED3D_LOCATION_TEXTURE_RGB)
353
                    wined3d_texture_bind_and_dirtify(volume->container, context, FALSE);
354
                else
355
                    wined3d_texture_bind_and_dirtify(volume->container, context, TRUE);
356

357 358 359 360 361 362 363 364 365 366 367
                wined3d_volume_download_data(volume, context, &data);
            }
            else
            {
                FIXME("Implement WINED3D_LOCATION_BUFFER loading from %s.\n",
                        wined3d_debug_location(volume->locations));
                return;
            }
            wined3d_volume_validate_location(volume, WINED3D_LOCATION_BUFFER);
            break;

368 369 370 371 372 373
        default:
            FIXME("Implement %s loading from %s.\n", wined3d_debug_location(location),
                    wined3d_debug_location(volume->locations));
    }
}

374 375 376
/* Context activation is done by the caller. */
void wined3d_volume_load(struct wined3d_volume *volume, struct wined3d_context *context, BOOL srgb_mode)
{
377 378 379
    wined3d_texture_prepare_texture(volume->container, context, srgb_mode);
    wined3d_volume_load_location(volume, context,
            srgb_mode ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB);
380 381
}

382 383 384 385 386 387 388 389
/* Context activation is done by the caller. */
static void wined3d_volume_prepare_pbo(struct wined3d_volume *volume, struct wined3d_context *context)
{
    const struct wined3d_gl_info *gl_info = context->gl_info;

    if (volume->pbo)
        return;

390 391 392 393
    GL_EXTCALL(glGenBuffers(1, &volume->pbo));
    GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, volume->pbo));
    GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, volume->resource.size, NULL, GL_STREAM_DRAW));
    GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
394 395 396 397 398 399 400 401 402 403 404
    checkGLcall("Create PBO");

    TRACE("Created PBO %u for volume %p.\n", volume->pbo, volume);
}

static void wined3d_volume_free_pbo(struct wined3d_volume *volume)
{
    struct wined3d_context *context = context_acquire(volume->resource.device, NULL);
    const struct wined3d_gl_info *gl_info = context->gl_info;

    TRACE("Deleting PBO %u belonging to volume %p.\n", volume->pbo, volume);
405 406
    GL_EXTCALL(glDeleteBuffers(1, &volume->pbo));
    checkGLcall("glDeleteBuffers");
407 408 409 410
    volume->pbo = 0;
    context_release(context);
}

411 412 413 414 415 416 417 418 419 420 421 422
void wined3d_volume_destroy(struct wined3d_volume *volume)
{
    TRACE("volume %p.\n", volume);

    if (volume->pbo)
        wined3d_volume_free_pbo(volume);

    resource_cleanup(&volume->resource);
    volume->resource.parent_ops->wined3d_object_destroyed(volume->resource.parent);
    HeapFree(GetProcessHeap(), 0, volume);
}

423
static void volume_unload(struct wined3d_resource *resource)
424
{
425
    struct wined3d_volume *volume = volume_from_resource(resource);
426 427 428 429 430
    struct wined3d_device *device = volume->resource.device;
    struct wined3d_context *context;

    if (volume->resource.pool == WINED3D_POOL_DEFAULT)
        ERR("Unloading DEFAULT pool volume.\n");
431

432 433
    TRACE("texture %p.\n", resource);

434 435 436 437 438 439 440 441 442 443 444 445 446
    if (volume_prepare_system_memory(volume))
    {
        context = context_acquire(device, NULL);
        wined3d_volume_load_location(volume, context, WINED3D_LOCATION_SYSMEM);
        context_release(context);
        wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_SYSMEM);
    }
    else
    {
        ERR("Out of memory when unloading volume %p.\n", volume);
        wined3d_volume_validate_location(volume, WINED3D_LOCATION_DISCARDED);
        wined3d_volume_invalidate_location(volume, ~WINED3D_LOCATION_DISCARDED);
    }
447

448 449 450 451 452 453 454 455 456
    if (volume->pbo)
    {
        /* Should not happen because only dynamic default pool volumes
         * have a buffer, and those are not evicted by device_evit_managed_resources
         * and must be freed before a non-ex device reset. */
        ERR("Unloading a volume with a buffer\n");
        wined3d_volume_free_pbo(volume);
    }

457
    /* The texture name is managed by the container. */
458
    volume->flags &= ~WINED3D_VFLAG_CLIENT_STORAGE;
459 460 461 462

    resource_unload(resource);
}

463
ULONG CDECL wined3d_volume_incref(struct wined3d_volume *volume)
464
{
465
    TRACE("Forwarding to container %p.\n", volume->container);
466

467
    return wined3d_texture_incref(volume->container);
468 469
}

470 471
ULONG CDECL wined3d_volume_decref(struct wined3d_volume *volume)
{
472
    TRACE("Forwarding to container %p.\n", volume->container);
473

474
    return wined3d_texture_decref(volume->container);
475 476
}

477
void * CDECL wined3d_volume_get_parent(const struct wined3d_volume *volume)
478
{
479
    TRACE("volume %p.\n", volume);
480

481
    return volume->resource.parent;
482 483
}

484
struct wined3d_resource * CDECL wined3d_volume_get_resource(struct wined3d_volume *volume)
485
{
486
    TRACE("volume %p.\n", volume);
487

488
    return &volume->resource;
489 490
}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
static BOOL volume_check_block_align(const struct wined3d_volume *volume,
        const struct wined3d_box *box)
{
    UINT width_mask, height_mask;
    const struct wined3d_format *format = volume->resource.format;

    if (!box)
        return TRUE;

    /* This assumes power of two block sizes, but NPOT block sizes would be
     * silly anyway.
     *
     * This also assumes that the format's block depth is 1. */
    width_mask = format->block_width - 1;
    height_mask = format->block_height - 1;

    if (box->left & width_mask)
        return FALSE;
    if (box->top & height_mask)
        return FALSE;
    if (box->right & width_mask && box->right != volume->resource.width)
        return FALSE;
    if (box->bottom & height_mask && box->bottom != volume->resource.height)
        return FALSE;

    return TRUE;
}

519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
static BOOL wined3d_volume_check_box_dimensions(const struct wined3d_volume *volume,
        const struct wined3d_box *box)
{
    if (!box)
        return TRUE;

    if (box->left >= box->right)
        return FALSE;
    if (box->top >= box->bottom)
        return FALSE;
    if (box->front >= box->back)
        return FALSE;
    if (box->right > volume->resource.width)
        return FALSE;
    if (box->bottom > volume->resource.height)
        return FALSE;
    if (box->back > volume->resource.depth)
        return FALSE;

    return TRUE;
}

541
HRESULT CDECL wined3d_volume_map(struct wined3d_volume *volume,
542
        struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
543
{
544 545 546 547
    struct wined3d_device *device = volume->resource.device;
    struct wined3d_context *context;
    const struct wined3d_gl_info *gl_info;
    BYTE *base_memory;
548
    const struct wined3d_format *format = volume->resource.format;
549
    const unsigned int fmt_flags = volume->container->resource.format_flags;
550

551 552
    TRACE("volume %p, map_desc %p, box %p, flags %#x.\n",
            volume, map_desc, box, flags);
553

554
    map_desc->data = NULL;
555 556 557
    if (!(volume->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
    {
        WARN("Volume %p is not CPU accessible.\n", volume);
558 559 560 561 562
        return WINED3DERR_INVALIDCALL;
    }
    if (volume->resource.map_count)
    {
        WARN("Volume is already mapped.\n");
563 564
        return WINED3DERR_INVALIDCALL;
    }
565 566 567 568 569
    if (!wined3d_volume_check_box_dimensions(volume, box))
    {
        WARN("Map box is invalid.\n");
        return WINED3DERR_INVALIDCALL;
    }
570
    if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !volume_check_block_align(volume, box))
571 572 573 574 575 576
    {
        WARN("Map box is misaligned for %ux%u blocks.\n",
                format->block_width, format->block_height);
        return WINED3DERR_INVALIDCALL;
    }

577
    flags = wined3d_resource_sanitize_map_flags(&volume->resource, flags);
578

579
    if (volume->resource.map_binding == WINED3D_LOCATION_BUFFER)
580
    {
581 582 583 584
        context = context_acquire(device, NULL);
        gl_info = context->gl_info;

        wined3d_volume_prepare_pbo(volume, context);
585 586 587 588
        if (flags & WINED3D_MAP_DISCARD)
            wined3d_volume_validate_location(volume, WINED3D_LOCATION_BUFFER);
        else
            wined3d_volume_load_location(volume, context, WINED3D_LOCATION_BUFFER);
589

590
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, volume->pbo));
591 592 593 594 595

        if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
        {
            GLbitfield mapflags = wined3d_resource_gl_map_flags(flags);
            mapflags &= ~GL_MAP_FLUSH_EXPLICIT_BIT;
596
            base_memory = GL_EXTCALL(glMapBufferRange(GL_PIXEL_UNPACK_BUFFER,
597 598 599 600
                    0, volume->resource.size, mapflags));
        }
        else
        {
601
            GLenum access = wined3d_resource_gl_legacy_map_flags(flags);
602
            base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, access));
603 604
        }

605
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
606
        checkGLcall("Map PBO");
607

608 609
        context_release(context);
    }
610 611
    else
    {
612 613 614 615 616 617 618 619 620 621 622 623
        if (!volume_prepare_system_memory(volume))
        {
            WARN("Out of memory.\n");
            map_desc->data = NULL;
            return E_OUTOFMEMORY;
        }

        if (flags & WINED3D_MAP_DISCARD)
        {
            wined3d_volume_validate_location(volume, WINED3D_LOCATION_SYSMEM);
        }
        else if (!(volume->locations & WINED3D_LOCATION_SYSMEM))
624 625 626 627 628
        {
            context = context_acquire(device, NULL);
            wined3d_volume_load_location(volume, context, WINED3D_LOCATION_SYSMEM);
            context_release(context);
        }
629
        base_memory = volume->resource.heap_memory;
630
    }
631

632
    TRACE("Base memory pointer %p.\n", base_memory);
633

634
    if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
635 636 637 638 639 640 641 642 643
    {
        map_desc->row_pitch = volume->resource.width * format->byte_count;
        map_desc->slice_pitch = map_desc->row_pitch * volume->resource.height;
    }
    else
    {
        wined3d_volume_get_pitch(volume, &map_desc->row_pitch, &map_desc->slice_pitch);
    }

644 645
    if (!box)
    {
646
        TRACE("No box supplied - all is ok\n");
647
        map_desc->data = base_memory;
648 649 650
    }
    else
    {
651 652
        TRACE("Lock Box (%p) = l %u, t %u, r %u, b %u, fr %u, ba %u\n",
                box, box->left, box->top, box->right, box->bottom, box->front, box->back);
653

654
        if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
        {
            /* Compressed textures are block based, so calculate the offset of
             * the block that contains the top-left pixel of the locked rectangle. */
            map_desc->data = base_memory
                    + (box->front * map_desc->slice_pitch)
                    + ((box->top / format->block_height) * map_desc->row_pitch)
                    + ((box->left / format->block_width) * format->block_byte_count);
        }
        else
        {
            map_desc->data = base_memory
                    + (map_desc->slice_pitch * box->front)
                    + (map_desc->row_pitch * box->top)
                    + (box->left * volume->resource.format->byte_count);
        }
670
    }
671

672
    if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
673
    {
674
        wined3d_texture_set_dirty(volume->container);
675
        wined3d_volume_invalidate_location(volume, ~volume->resource.map_binding);
676 677
    }

678
    volume->resource.map_count++;
679 680

    TRACE("Returning memory %p, row pitch %d, slice pitch %d.\n",
681
            map_desc->data, map_desc->row_pitch, map_desc->slice_pitch);
682

683
    return WINED3D_OK;
684 685
}

686
struct wined3d_volume * CDECL wined3d_volume_from_resource(struct wined3d_resource *resource)
687
{
688
    return volume_from_resource(resource);
689 690
}

691
HRESULT CDECL wined3d_volume_unmap(struct wined3d_volume *volume)
692
{
693 694
    TRACE("volume %p.\n", volume);

695
    if (!volume->resource.map_count)
696
    {
697
        WARN("Trying to unlock an unlocked volume %p.\n", volume);
698
        return WINED3DERR_INVALIDCALL;
699
    }
700

701
    if (volume->resource.map_binding == WINED3D_LOCATION_BUFFER)
702 703 704 705 706
    {
        struct wined3d_device *device = volume->resource.device;
        struct wined3d_context *context = context_acquire(device, NULL);
        const struct wined3d_gl_info *gl_info = context->gl_info;

707 708 709
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, volume->pbo));
        GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
        GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
710 711 712 713 714
        checkGLcall("Unmap PBO");

        context_release(context);
    }

715
    volume->resource.map_count--;
716

717
    return WINED3D_OK;
718 719
}

720 721 722 723 724 725 726 727 728 729
static ULONG volume_resource_incref(struct wined3d_resource *resource)
{
    return wined3d_volume_incref(volume_from_resource(resource));
}

static ULONG volume_resource_decref(struct wined3d_resource *resource)
{
    return wined3d_volume_decref(volume_from_resource(resource));
}

730 731
static const struct wined3d_resource_ops volume_resource_ops =
{
732 733
    volume_resource_incref,
    volume_resource_decref,
734 735 736
    volume_unload,
};

737 738
static HRESULT volume_init(struct wined3d_volume *volume, struct wined3d_texture *container,
        const struct wined3d_resource_desc *desc, UINT level)
739
{
740
    struct wined3d_device *device = container->resource.device;
741
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
742
    const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
743
    HRESULT hr;
744
    UINT size;
745

746 747
    /* TODO: Write tests for other resources and move this check
     * to resource_init, if applicable. */
748 749
    if (desc->usage & WINED3DUSAGE_DYNAMIC
            && (desc->pool == WINED3D_POOL_MANAGED || desc->pool == WINED3D_POOL_SCRATCH))
750
    {
751
        WARN("Attempted to create a DYNAMIC texture in pool %s.\n", debug_d3dpool(desc->pool));
752 753
        return WINED3DERR_INVALIDCALL;
    }
754

755
    size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, desc->depth);
756

757 758
    if (FAILED(hr = resource_init(&volume->resource, device, WINED3D_RTYPE_VOLUME, format,
            WINED3D_MULTISAMPLE_NONE, 0, desc->usage, desc->pool, desc->width, desc->height, desc->depth,
759
            size, NULL, &wined3d_null_parent_ops, &volume_resource_ops)))
760 761 762 763 764
    {
        WARN("Failed to initialize resource, returning %#x.\n", hr);
        return hr;
    }

765
    volume->texture_level = level;
766
    volume->locations = WINED3D_LOCATION_DISCARDED;
767

768
    if (desc->pool == WINED3D_POOL_DEFAULT && desc->usage & WINED3DUSAGE_DYNAMIC
769 770
            && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
            && !format->convert)
771
    {
772
        wined3d_resource_free_sysmem(&volume->resource);
773
        volume->resource.map_binding = WINED3D_LOCATION_BUFFER;
774 775
    }

776
    volume->container = container;
777

778 779
    return WINED3D_OK;
}
780

781 782
HRESULT wined3d_volume_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
        unsigned int level, struct wined3d_volume **volume)
783
{
784
    struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
785
    const struct wined3d_parent_ops *parent_ops;
786
    struct wined3d_volume *object;
787
    void *parent;
788 789
    HRESULT hr;

790
    TRACE("container %p, width %u, height %u, depth %u, level %u, format %s, "
791
            "usage %#x, pool %s, volume %p.\n",
792
            container, desc->width, desc->height, desc->depth, level, debug_d3dformat(desc->format),
793
            desc->usage, debug_d3dpool(desc->pool), volume);
794

795 796
    if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
        return E_OUTOFMEMORY;
797

798
    if (FAILED(hr = volume_init(object, container, desc, level)))
799 800 801 802 803 804
    {
        WARN("Failed to initialize volume, returning %#x.\n", hr);
        HeapFree(GetProcessHeap(), 0, object);
        return hr;
    }

805 806
    if (FAILED(hr = device_parent->ops->volume_created(device_parent,
            wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
807 808
    {
        WARN("Failed to create volume parent, hr %#x.\n", hr);
809
        wined3d_volume_destroy(object);
810 811 812 813 814 815 816
        return hr;
    }

    TRACE("Created volume %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);

    object->resource.parent = parent;
    object->resource.parent_ops = parent_ops;
817 818 819 820
    *volume = object;

    return WINED3D_OK;
}