buffer.c 38.5 KB
Newer Older
1
/*
2 3 4
 * Copyright 2002-2005 Jason Edmeades
 * Copyright 2002-2005 Raphael Junqueira
 * Copyright 2004 Christian Costa
5
 * Copyright 2005 Oliver Stieber
6
 * Copyright 2007 Stefan Dösinger for CodeWeavers
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 * Copyright 2009 Henri Verbeet for CodeWeavers
 *
 * 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */

#include "config.h"
#include "wine/port.h"

#include "wined3d_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(d3d);

32 33 34 35 36
#define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info

#define VB_MAXDECLCHANGES     100     /* After that number we stop converting */
#define VB_RESETDECLCHANGE    1000    /* Reset the changecount after that number of draws */

37
static void buffer_create_buffer_object(struct wined3d_buffer *This)
38
{
39 40
    GLenum error, gl_usage;
    IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
41

42 43
    TRACE("Creating an OpenGL vertex buffer object for IWineD3DVertexBuffer %p Usage(%s)\n",
            This, debug_d3dusage(This->resource.usage));
44

45 46 47
    /* Make sure that a context is there. Needed in a multithreaded environment. Otherwise this call is a nop */
    ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
    ENTER_GL();
48

49 50 51 52 53 54
    /* Make sure that the gl error is cleared. Do not use checkGLcall
    * here because checkGLcall just prints a fixme and continues. However,
    * if an error during VBO creation occurs we can fall back to non-vbo operation
    * with full functionality(but performance loss)
    */
    while (glGetError() != GL_NO_ERROR);
55

56
    /* Basically the FVF parameter passed to CreateVertexBuffer is no good
57 58 59 60 61
     * It is the FVF set with IWineD3DDevice::SetFVF or the Vertex Declaration set with
     * IWineD3DDevice::SetVertexDeclaration that decides how the vertices in the buffer
     * look like. This means that on each DrawPrimitive call the vertex buffer has to be verified
     * to check if the rhw and color values are in the correct format.
     */
62

63 64 65
    GL_EXTCALL(glGenBuffersARB(1, &This->buffer_object));
    error = glGetError();
    if (!This->buffer_object || error != GL_NO_ERROR)
66
    {
67 68
        ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error);
        goto fail;
69 70
    }

71 72 73 74 75
    if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
    {
        IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER);
    }
    GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
76 77
    error = glGetError();
    if (error != GL_NO_ERROR)
78
    {
79 80
        ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
        goto fail;
81 82
    }

83 84 85 86 87
    /* Don't use static, because dx apps tend to update the buffer
    * quite often even if they specify 0 usage. Because we always keep the local copy
    * we never read from the vbo and can create a write only opengl buffer.
    */
    switch(This->resource.usage & (WINED3DUSAGE_WRITEONLY | WINED3DUSAGE_DYNAMIC))
88
    {
89 90 91 92 93
        case WINED3DUSAGE_WRITEONLY | WINED3DUSAGE_DYNAMIC:
        case WINED3DUSAGE_DYNAMIC:
            TRACE("Gl usage = GL_STREAM_DRAW\n");
            gl_usage = GL_STREAM_DRAW_ARB;
            break;
94

95 96 97 98 99
        case WINED3DUSAGE_WRITEONLY:
        default:
            TRACE("Gl usage = GL_DYNAMIC_DRAW\n");
            gl_usage = GL_DYNAMIC_DRAW_ARB;
            break;
100 101
    }

102
    /* Reserve memory for the buffer. The amount of data won't change
103 104 105 106 107
     * so we are safe with calling glBufferData once and
     * calling glBufferSubData on updates. Upload the actual data in case
     * we're not double buffering, so we can release the heap mem afterwards
     */
    GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage));
108 109
    error = glGetError();
    if (error != GL_NO_ERROR)
110
    {
111 112
        ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error);
        goto fail;
113 114
    }

115
    LEAVE_GL();
116

117 118 119 120
    This->buffer_object_size = This->resource.size;
    This->buffer_object_usage = gl_usage;
    This->dirty_start = 0;
    This->dirty_end = This->resource.size;
121 122 123 124 125 126 127 128 129 130 131 132

    if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)
    {
        This->flags |= WINED3D_BUFFER_DIRTY;
    }
    else
    {
        HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
        This->resource.allocatedMemory = NULL;
        This->resource.heapMemory = NULL;
        This->flags &= ~WINED3D_BUFFER_DIRTY;
    }
133

134
    return;
135

136 137 138 139 140 141
fail:
    /* Clean up all vbo init, but continue because we can work without a vbo :-) */
    ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n");
    if (This->buffer_object) GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
    This->buffer_object = 0;
    LEAVE_GL();
142

143
    return;
144 145
}

146 147
static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This,
        const enum wined3d_buffer_conversion_type conversion_type,
148
        const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run)
149 150 151
{
    DWORD attrib_size;
    BOOL ret = FALSE;
152
    unsigned int i;
153
    DWORD offset = This->resource.wineD3DDevice->stateBlock->streamOffset[attrib->stream_idx];
154 155 156 157 158 159 160
    DWORD_PTR data;

    /* Check for some valid situations which cause us pain. One is if the buffer is used for
     * constant attributes(stride = 0), the other one is if the buffer is used on two streams
     * with different strides. In the 2nd case we might have to drop conversion entirely,
     * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N.
     */
161
    if (!attrib->stride)
162 163
    {
        FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else\n",
164
                debug_d3dformat(attrib->format_desc->format));
165
    }
166
    else if(attrib->stride != *stride_this_run && *stride_this_run)
167
    {
168
        FIXME("Got two concurrent strides, %d and %d\n", attrib->stride, *stride_this_run);
169 170 171
    }
    else
    {
172
        *stride_this_run = attrib->stride;
173 174 175 176 177 178 179
        if (This->stride != *stride_this_run)
        {
            /* We rely that this happens only on the first converted attribute that is found,
             * if at all. See above check
             */
            TRACE("Reconverting because converted attributes occur, and the stride changed\n");
            This->stride = *stride_this_run;
180 181 182
            HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, This->conversion_map);
            This->conversion_map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                    sizeof(*This->conversion_map) * This->stride);
183 184 185 186
            ret = TRUE;
        }
    }

187
    data = (((DWORD_PTR)attrib->data) + offset) % This->stride;
188
    attrib_size = attrib->format_desc->component_count * attrib->format_desc->component_size;
189 190
    for (i = 0; i < attrib_size; ++i)
    {
191
        if (This->conversion_map[data + i] != conversion_type)
192 193
        {
            TRACE("Byte %ld in vertex changed\n", i + data);
194
            TRACE("It was type %d, is %d now\n", This->conversion_map[data + i], conversion_type);
195
            ret = TRUE;
196
            This->conversion_map[data + i] = conversion_type;
197 198 199 200 201 202
        }
    }

    return ret;
}

203
static BOOL buffer_check_attribute(struct wined3d_buffer *This,
204
        const struct wined3d_stream_info_element *attrib, const BOOL check_d3dcolor, const BOOL is_ffp_position,
205 206 207
        const BOOL is_ffp_color, DWORD *stride_this_run, BOOL *float16_used)
{
    BOOL ret = FALSE;
208
    WINED3DFORMAT format;
209 210 211 212

    /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is
     * there, on nonexistent attribs the vbo is 0.
     */
213
    if (attrib->buffer_object != This->buffer_object) return FALSE;
214

215
    format = attrib->format_desc->format;
216
    /* Look for newly appeared conversion */
217
    if (!GL_SUPPORT(ARB_HALF_FLOAT_VERTEX) && (format == WINED3DFMT_R16G16_FLOAT || format == WINED3DFMT_R16G16B16A16_FLOAT))
218
    {
219
        ret = buffer_process_converted_attribute(This, CONV_FLOAT16_2, attrib, stride_this_run);
220 221 222 223 224

        if (is_ffp_position) FIXME("Test FLOAT16 fixed function processing positions\n");
        else if (is_ffp_color) FIXME("test FLOAT16 fixed function processing colors\n");
        *float16_used = TRUE;
    }
225
    else if (check_d3dcolor && format == WINED3DFMT_A8R8G8B8)
226
    {
227
        ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run);
228

229
        if (!is_ffp_color) FIXME("Test for non-color fixed function WINED3DFMT_A8R8G8B8 format\n");
230
    }
231
    else if (is_ffp_position && format == WINED3DFMT_R32G32B32A32_FLOAT)
232
    {
233
        ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run);
234 235 236
    }
    else if (This->conversion_map)
    {
237
        ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run);
238 239 240 241 242 243
    }

    return ret;
}

static UINT *find_conversion_shift(struct wined3d_buffer *This,
244
        const struct wined3d_stream_info *strided, UINT stride)
245 246 247 248 249 250 251 252 253 254 255 256 257
{
    UINT *ret, i, j, shift, orig_type_size;

    if (!stride)
    {
        TRACE("No shift\n");
        return NULL;
    }

    This->conversion_stride = stride;
    ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DWORD) * stride);
    for (i = 0; i < MAX_ATTRIBS; ++i)
    {
258 259
        WINED3DFORMAT format;

260
        if (strided->elements[i].buffer_object != This->buffer_object) continue;
261

262
        format = strided->elements[i].format_desc->format;
263
        if (format == WINED3DFMT_R16G16_FLOAT)
264 265 266
        {
            shift = 4;
        }
267
        else if (format == WINED3DFMT_R16G16B16A16_FLOAT)
268 269 270 271 272 273 274
        {
            shift = 8;
            /* Pre-shift the last 4 bytes in the FLOAT16_4 by 4 bytes - this makes FLOAT16_2 and FLOAT16_4 conversions
             * compatible
             */
            for (j = 4; j < 8; ++j)
            {
275
                ret[(DWORD_PTR)strided->elements[i].data + j] += 4;
276 277 278 279 280 281 282 283 284 285
            }
        }
        else
        {
            shift = 0;
        }
        This->conversion_stride += shift;

        if (shift)
        {
286 287
            orig_type_size = strided->elements[i].format_desc->component_count
                    * strided->elements[i].format_desc->component_size;
288
            for (j = (DWORD_PTR)strided->elements[i].data + orig_type_size; j < stride; ++j)
289 290 291 292 293
            {
                ret[j] += shift;
            }
        }
    }
294

295
    if (TRACE_ON(d3d))
296
    {
297 298 299 300 301 302
        TRACE("Dumping conversion shift:\n");
        for (i = 0; i < stride; ++i)
        {
            TRACE("[%d]", ret[i]);
        }
        TRACE("\n");
303 304 305 306 307
    }

    return ret;
}

308
static BOOL buffer_find_decl(struct wined3d_buffer *This)
309 310
{
    IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
311
    UINT stride_this_run = 0;
312
    BOOL float16_used = FALSE;
313 314
    BOOL ret = FALSE;
    unsigned int i;
315 316

    /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
317 318
     * Once we have our declaration there is no need to look it up again. Index buffers also never need
     * conversion, so once the (empty) conversion structure is created don't bother checking again
319
     */
320 321 322 323 324
    if (This->flags & WINED3D_BUFFER_HASDESC)
    {
        if(((IWineD3DImpl *)device->wineD3D)->dxVersion == 7 ||
             This->resource.format_desc->format != WINED3DFMT_VERTEXDATA) return FALSE;
    }
325 326 327 328 329

    TRACE("Finding vertex buffer conversion information\n");
    /* Certain declaration types need some fixups before we can pass them to
     * opengl. This means D3DCOLOR attributes with fixed function vertex
     * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
330
     * GL_ARB_half_float_vertex is not supported.
331
     *
332
     * Note for d3d8 and d3d9:
333 334 335 336 337
     * The vertex buffer FVF doesn't help with finding them, we have to use
     * the decoded vertex declaration and pick the things that concern the
     * current buffer. A problem with this is that this can change between
     * draws, so we have to validate the information and reprocess the buffer
     * if it changes, and avoid false positives for performance reasons.
338 339 340
     * WineD3D doesn't even know the vertex buffer any more, it is managed
     * by the client libraries and passed to SetStreamSource and ProcessVertices
     * as needed.
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
     *
     * We have to distinguish between vertex shaders and fixed function to
     * pick the way we access the strided vertex information.
     *
     * This code sets up a per-byte array with the size of the detected
     * stride of the arrays in the buffer. For each byte we have a field
     * that marks the conversion needed on this byte. For example, the
     * following declaration with fixed function vertex processing:
     *
     *      POSITIONT, FLOAT4
     *      NORMAL, FLOAT3
     *      DIFFUSE, FLOAT16_4
     *      SPECULAR, D3DCOLOR
     *
     * Will result in
     * {                 POSITIONT                    }{             NORMAL                }{    DIFFUSE          }{SPECULAR }
     * [P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][0][0][0][0][0][0][0][0][0][0][0][0][F][F][F][F][F][F][F][F][C][C][C][C]
     *
     * Where in this example map P means 4 component position conversion, 0
     * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
     * conversion (red / blue swizzle).
     *
     * If we're doing conversion and the stride changes we have to reconvert
     * the whole buffer. Note that we do not mind if the semantic changes,
     * we only care for the conversion type. So if the NORMAL is replaced
     * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
     * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
     * conversion types depend on the semantic as well, for example a FLOAT4
     * texcoord needs no conversion while a FLOAT4 positiont needs one
     */
    if (use_vs(device->stateBlock))
    {
        TRACE("vshader\n");
        /* If the current vertex declaration is marked for no half float conversion don't bother to
         * analyse the strided streams in depth, just set them up for no conversion. Return decl changed
         * if we used conversion before
         */
        if (!((IWineD3DVertexDeclarationImpl *) device->stateBlock->vertexDecl)->half_float_conv_needed)
        {
380
            if (This->conversion_map)
381 382
            {
                TRACE("Now using shaders without conversion, but conversion used before\n");
383 384 385
                HeapFree(GetProcessHeap(), 0, This->conversion_map);
                HeapFree(GetProcessHeap(), 0, This->conversion_shift);
                This->conversion_map = NULL;
386
                This->stride = 0;
387 388
                This->conversion_shift = NULL;
                This->conversion_stride = 0;
389 390 391 392 393 394 395 396 397
                return TRUE;
            }
            else
            {
                return FALSE;
            }
        }
        for (i = 0; i < MAX_ATTRIBS; ++i)
        {
398
            ret = buffer_check_attribute(This, &device->strided_streams.elements[i],
399 400 401 402 403 404
                    FALSE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
        }

        /* Recalculate the conversion shift map if the declaration has changed,
         * and we're using float16 conversion or used it on the last run
         */
405
        if (ret && (float16_used || This->conversion_map))
406
        {
407 408
            HeapFree(GetProcessHeap(), 0, This->conversion_shift);
            This->conversion_shift = find_conversion_shift(This, &device->strided_streams, This->stride);
409 410 411 412 413 414 415 416 417
        }
    }
    else
    {
        /* Fixed function is a bit trickier. We have to take care for D3DCOLOR types, FLOAT4 positions and of course
         * FLOAT16s if not supported. Also, we can't iterate over the array, so use macros to generate code for all
         * the attributes that our current fixed function pipeline implementation cares for.
         */
        BOOL support_d3dcolor = GL_SUPPORT(EXT_VERTEX_ARRAY_BGRA);
418
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_POSITION],
419
                TRUE, TRUE,  FALSE, &stride_this_run, &float16_used) || ret;
420
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_NORMAL],
421
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
422
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_DIFFUSE],
423
                !support_d3dcolor, FALSE, TRUE,  &stride_this_run, &float16_used) || ret;
424
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_SPECULAR],
425
                !support_d3dcolor, FALSE, TRUE,  &stride_this_run, &float16_used) || ret;
426
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD0],
427
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
428
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD1],
429
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
430
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD2],
431
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
432
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD3],
433
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
434
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD4],
435
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
436
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD5],
437
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
438
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD6],
439
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;
440
        ret = buffer_check_attribute(This, &device->strided_streams.elements[WINED3D_FFP_TEXCOORD7],
441 442 443 444 445
                TRUE, FALSE, FALSE, &stride_this_run, &float16_used) || ret;

        if (float16_used) FIXME("Float16 conversion used with fixed function vertex processing\n");
    }

446
    if (stride_this_run == 0 && This->conversion_map)
447 448 449
    {
        /* Sanity test */
        if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
450 451
        HeapFree(GetProcessHeap(), 0, This->conversion_map);
        This->conversion_map = NULL;
452 453 454 455 456 457 458 459
        This->stride = 0;
    }

    if (ret) TRACE("Conversion information changed\n");

    return ret;
}

460
static void buffer_check_buffer_object_size(struct wined3d_buffer *This)
461
{
462 463 464
    UINT size = This->conversion_stride ?
            This->conversion_stride * (This->resource.size / This->stride) : This->resource.size;
    if (This->buffer_object_size != size)
465
    {
466
        TRACE("Old size %u, creating new size %u\n", This->buffer_object_size, size);
467 468 469 470 471

        if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
        {
            IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER);
        }
472 473 474 475 476 477 478

        /* Rescue the data before resizing the buffer object if we do not have our backup copy */
        if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER))
        {
            buffer_get_sysmem(This);
        }

479
        ENTER_GL();
480
        GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
481
        checkGLcall("glBindBufferARB");
482
        GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, size, NULL, This->buffer_object_usage));
483
        This->buffer_object_size = size;
484 485 486 487 488
        checkGLcall("glBufferDataARB");
        LEAVE_GL();
    }
}

489
static inline void fixup_d3dcolor(DWORD *dst_color)
490
{
491
    DWORD src_color = *dst_color;
492

493 494 495 496 497 498 499 500 501 502 503 504 505
    /* Color conversion like in drawStridedSlow. watch out for little endianity
     * If we want that stuff to work on big endian machines too we have to consider more things
     *
     * 0xff000000: Alpha mask
     * 0x00ff0000: Blue mask
     * 0x0000ff00: Green mask
     * 0x000000ff: Red mask
     */
    *dst_color = 0;
    *dst_color |= (src_color & 0xff00ff00);         /* Alpha Green */
    *dst_color |= (src_color & 0x00ff0000) >> 16;   /* Red */
    *dst_color |= (src_color & 0x000000ff) << 16;   /* Blue */
}
506

507 508 509
static inline void fixup_transformed_pos(float *p)
{
    float x, y, z, w;
510

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
    /* rhw conversion like in drawStridedSlow */
    if (p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps)))
    {
        x = p[0];
        y = p[1];
        z = p[2];
        w = 1.0;
    }
    else
    {
        w = 1.0 / p[3];
        x = p[0] * w;
        y = p[1] * w;
        z = p[2] * w;
    }
    p[0] = x;
    p[1] = y;
    p[2] = z;
    p[3] = w;
}
531

532 533 534
const BYTE *buffer_get_memory(IWineD3DBuffer *iface, UINT offset, GLuint *buffer_object)
{
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
535

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
    *buffer_object = This->buffer_object;
    if (!This->buffer_object)
    {
        if (This->flags & WINED3D_BUFFER_CREATEBO)
        {
            buffer_create_buffer_object(This);
            This->flags &= ~WINED3D_BUFFER_CREATEBO;
            if (This->buffer_object)
            {
                *buffer_object = This->buffer_object;
                return (const BYTE *)offset;
            }
        }
        return This->resource.allocatedMemory + offset;
    }
    else
552
    {
553
        return (const BYTE *)offset;
554
    }
555
}
556

557 558 559 560 561 562 563 564 565 566 567
/* IUnknown methods */

static HRESULT STDMETHODCALLTYPE buffer_QueryInterface(IWineD3DBuffer *iface,
        REFIID riid, void **object)
{
    TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object);

    if (IsEqualGUID(riid, &IID_IWineD3DBuffer)
            || IsEqualGUID(riid, &IID_IWineD3DResource)
            || IsEqualGUID(riid, &IID_IWineD3DBase)
            || IsEqualGUID(riid, &IID_IUnknown))
568
    {
569 570 571
        IUnknown_AddRef(iface);
        *object = iface;
        return S_OK;
572 573
    }

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
    WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));

    *object = NULL;

    return E_NOINTERFACE;
}

static ULONG STDMETHODCALLTYPE buffer_AddRef(IWineD3DBuffer *iface)
{
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
    ULONG refcount = InterlockedIncrement(&This->resource.ref);

    TRACE("%p increasing refcount to %u\n", This, refcount);

    return refcount;
}

591 592
const BYTE *buffer_get_sysmem(struct wined3d_buffer *This)
{
593 594
    /* AllocatedMemory exists if the buffer is double buffered or has no buffer object at all */
    if(This->resource.allocatedMemory) return This->resource.allocatedMemory;
595 596 597 598 599 600 601 602 603 604 605 606

    This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
    This->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
    ENTER_GL();
    GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
    GL_EXTCALL(glGetBufferSubDataARB(This->buffer_type_hint, 0, This->resource.size, This->resource.allocatedMemory));
    LEAVE_GL();
    This->flags |= WINED3D_BUFFER_DOUBLEBUFFER;

    return This->resource.allocatedMemory;
}

607 608 609 610 611 612 613
static void STDMETHODCALLTYPE buffer_UnLoad(IWineD3DBuffer *iface)
{
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;

    TRACE("iface %p\n", iface);

    if (This->buffer_object)
614
    {
615
        IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
616

617
        ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
618 619 620 621 622 623 624 625

        /* Download the buffer, but don't permanently enable double buffering */
        if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER))
        {
            buffer_get_sysmem(This);
            This->flags &= ~WINED3D_BUFFER_DOUBLEBUFFER;
        }

626 627 628 629 630 631
        ENTER_GL();
        GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
        checkGLcall("glDeleteBuffersARB");
        LEAVE_GL();
        This->buffer_object = 0;
        This->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */
632
    }
633
}
634

635 636 637 638 639 640 641 642
static ULONG STDMETHODCALLTYPE buffer_Release(IWineD3DBuffer *iface)
{
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
    ULONG refcount = InterlockedDecrement(&This->resource.ref);

    TRACE("%p decreasing refcount to %u\n", This, refcount);

    if (!refcount)
643
    {
644 645 646
        buffer_UnLoad(iface);
        resource_cleanup((IWineD3DResource *)iface);
        HeapFree(GetProcessHeap(), 0, This);
647 648
    }

649 650
    return refcount;
}
651

652
/* IWineD3DBase methods */
653

654 655 656 657
static HRESULT STDMETHODCALLTYPE buffer_GetParent(IWineD3DBuffer *iface, IUnknown **parent)
{
    return resource_get_parent((IWineD3DResource *)iface, parent);
}
658

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
/* IWineD3DResource methods */

static HRESULT STDMETHODCALLTYPE buffer_GetDevice(IWineD3DBuffer *iface, IWineD3DDevice **device)
{
    return resource_get_device((IWineD3DResource *)iface, device);
}

static HRESULT STDMETHODCALLTYPE buffer_SetPrivateData(IWineD3DBuffer *iface,
        REFGUID guid, const void *data, DWORD data_size, DWORD flags)
{
    return resource_set_private_data((IWineD3DResource *)iface, guid, data, data_size, flags);
}

static HRESULT STDMETHODCALLTYPE buffer_GetPrivateData(IWineD3DBuffer *iface,
        REFGUID guid, void *data, DWORD *data_size)
{
    return resource_get_private_data((IWineD3DResource *)iface, guid, data, data_size);
}

static HRESULT STDMETHODCALLTYPE buffer_FreePrivateData(IWineD3DBuffer *iface, REFGUID guid)
{
    return resource_free_private_data((IWineD3DResource *)iface, guid);
}

static DWORD STDMETHODCALLTYPE buffer_SetPriority(IWineD3DBuffer *iface, DWORD priority)
{
    return resource_set_priority((IWineD3DResource *)iface, priority);
}

static DWORD STDMETHODCALLTYPE buffer_GetPriority(IWineD3DBuffer *iface)
{
    return resource_get_priority((IWineD3DResource *)iface);
691 692
}

693
static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
694
{
695
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
696 697
    IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
    UINT start = 0, end = 0, vertices;
698 699 700 701 702
    BOOL decl_changed = FALSE;
    unsigned int i, j;
    BYTE *data;

    TRACE("iface %p\n", iface);
703

704
    if (!This->buffer_object)
705 706
    {
        /* TODO: Make converting independent from VBOs */
707
        if (This->flags & WINED3D_BUFFER_CREATEBO)
708
        {
709 710
            buffer_create_buffer_object(This);
            This->flags &= ~WINED3D_BUFFER_CREATEBO;
711 712 713 714 715 716 717 718
        }
        else
        {
            return; /* Not doing any conversion */
        }
    }

    /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
719
    if (device->isInDraw && This->bind_count > 0)
720
    {
721 722
        decl_changed = buffer_find_decl(This);
        This->flags |= WINED3D_BUFFER_HASDESC;
723
    }
724

725
    if (!decl_changed && !(This->flags & WINED3D_BUFFER_HASDESC && This->flags & WINED3D_BUFFER_DIRTY)) return;
726 727 728 729 730

    /* If applications change the declaration over and over, reconverting all the time is a huge
     * performance hit. So count the declaration changes and release the VBO if there are too many
     * of them (and thus stop converting)
     */
731
    if (decl_changed)
732
    {
733 734
        ++This->conversion_count;
        This->draw_count = 0;
735

736
        if (This->conversion_count > VB_MAXDECLCHANGES)
737 738 739 740
        {
            FIXME("Too many declaration changes, stopping converting\n");
            ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
            ENTER_GL();
741
            GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
742 743
            checkGLcall("glDeleteBuffersARB");
            LEAVE_GL();
744 745
            This->buffer_object = 0;
            HeapFree(GetProcessHeap(), 0, This->conversion_shift);
746 747 748 749 750 751 752 753 754 755

            /* The stream source state handler might have read the memory of the vertex buffer already
             * and got the memory in the vbo which is not valid any longer. Dirtify the stream source
             * to force a reload. This happens only once per changed vertexbuffer and should occur rather
             * rarely
             */
            IWineD3DDeviceImpl_MarkStateDirty(device, STATE_STREAMSRC);

            return;
        }
756
        buffer_check_buffer_object_size(This);
757 758 759 760 761 762 763
    }
    else
    {
        /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
         * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
         * decl changes and reset the decl change count after a specific number of them
         */
764 765
        ++This->draw_count;
        if (This->draw_count > VB_RESETDECLCHANGE) This->conversion_count = 0;
766 767
    }

768
    if (decl_changed)
769 770 771 772 773 774
    {
        /* The declaration changed, reload the whole buffer */
        WARN("Reloading buffer because of decl change\n");
        start = 0;
        end = This->resource.size;
    }
775
    else
776 777
    {
        /* No decl change, but dirty data, reload the changed stuff */
778
        if (This->conversion_shift)
779
        {
780
            if (This->dirty_start != 0 || This->dirty_end != 0)
781 782 783 784
            {
                FIXME("Implement partial buffer loading with shifted conversion\n");
            }
        }
785 786
        start = This->dirty_start;
        end = This->dirty_end;
787 788 789
    }

    /* Mark the buffer clean */
790 791 792
    This->flags &= ~WINED3D_BUFFER_DIRTY;
    This->dirty_start = 0;
    This->dirty_end = 0;
793

794 795 796 797 798
    if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
    {
        IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER);
    }

799
    if (!This->conversion_map)
800 801 802 803 804 805 806
    {
        /* That means that there is nothing to fixup. Just upload from This->resource.allocatedMemory
         * directly into the vbo. Do not free the system memory copy because drawPrimitive may need it if
         * the stride is 0, for instancing emulation, vertex blending emulation or shader emulation.
         */
        TRACE("No conversion needed\n");

807 808 809
        /* Nothing to do because we locked directly into the vbo */
        if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)) return;

810 811 812 813 814
        if (!device->isInDraw)
        {
            ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
        }
        ENTER_GL();
815
        GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
816
        checkGLcall("glBindBufferARB");
817
        GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end-start, This->resource.allocatedMemory + start));
818 819 820 821 822
        checkGLcall("glBufferSubDataARB");
        LEAVE_GL();
        return;
    }

823 824 825 826 827
    if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER))
    {
        buffer_get_sysmem(This);
    }

828 829 830
    /* Now for each vertex in the buffer that needs conversion */
    vertices = This->resource.size / This->stride;

831
    if (This->conversion_shift)
832 833
    {
        TRACE("Shifted conversion\n");
834
        data = HeapAlloc(GetProcessHeap(), 0, vertices * This->conversion_stride);
835 836 837 838 839

        for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i)
        {
            for (j = 0; j < This->stride; ++j)
            {
840
                switch(This->conversion_map[j])
841 842
                {
                    case CONV_NONE:
843 844
                        data[This->conversion_stride * i + j + This->conversion_shift[j]]
                                = This->resource.allocatedMemory[This->stride * i + j];
845 846 847 848
                        break;

                    case CONV_FLOAT16_2:
                    {
849
                        float *out = (float *)(&data[This->conversion_stride * i + j + This->conversion_shift[j]]);
850 851 852 853 854 855 856 857 858
                        const WORD *in = (WORD *)(&This->resource.allocatedMemory[i * This->stride + j]);

                        out[1] = float_16_to_32(in + 1);
                        out[0] = float_16_to_32(in + 0);
                        j += 3;    /* Skip 3 additional bytes,as a FLOAT16_2 has 4 bytes */
                        break;
                    }

                    default:
859 860
                        FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
                        break;
861 862 863 864 865
                }
            }
        }

        ENTER_GL();
866
        GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
867
        checkGLcall("glBindBufferARB");
868
        GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, 0, vertices * This->conversion_stride, data));
869 870 871 872 873 874 875 876 877 878 879
        checkGLcall("glBufferSubDataARB");
        LEAVE_GL();
    }
    else
    {
        data = HeapAlloc(GetProcessHeap(), 0, This->resource.size);
        memcpy(data + start, This->resource.allocatedMemory + start, end - start);
        for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i)
        {
            for (j = 0; j < This->stride; ++j)
            {
880
                switch(This->conversion_map[j])
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
                {
                    case CONV_NONE:
                        /* Done already */
                        j += 3;
                        break;
                    case CONV_D3DCOLOR:
                        fixup_d3dcolor((DWORD *) (data + i * This->stride + j));
                        j += 3;
                        break;

                    case CONV_POSITIONT:
                        fixup_transformed_pos((float *) (data + i * This->stride + j));
                        j += 15;
                        break;

                    case CONV_FLOAT16_2:
                        ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
                    default:
899
                        FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
900 901 902 903 904
                }
            }
        }

        ENTER_GL();
905
        GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
906
        checkGLcall("glBindBufferARB");
907
        GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end - start, data + start));
908 909 910 911 912 913 914
        checkGLcall("glBufferSubDataARB");
        LEAVE_GL();
    }

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

915
static WINED3DRESOURCETYPE STDMETHODCALLTYPE buffer_GetType(IWineD3DBuffer *iface)
916 917 918 919
{
    return resource_get_type((IWineD3DResource *)iface);
}

920
/* IWineD3DBuffer methods */
921

922
static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset, UINT size, BYTE **data, DWORD flags)
923
{
924
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
925
    LONG count;
926

927
    TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface, offset, size, data, flags);
928

929
    count = InterlockedIncrement(&This->lock_count);
930

931
    if (This->flags & WINED3D_BUFFER_DIRTY)
932
    {
933 934 935
        if (This->dirty_start > offset) This->dirty_start = offset;

        if (size)
936
        {
937
            if (This->dirty_end < offset + size) This->dirty_end = offset + size;
938 939 940
        }
        else
        {
941
            This->dirty_end = This->resource.size;
942 943 944 945
        }
    }
    else
    {
946 947 948
        This->dirty_start = offset;
        if (size) This->dirty_end = offset + size;
        else This->dirty_end = This->resource.size;
949 950
    }

951 952 953 954
    if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object)
    {
        if(count == 1)
        {
955 956
            IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;

957 958 959 960
            if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
            {
                IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER);
            }
961 962 963

            ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
            ENTER_GL();
964 965
            GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
            This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_READ_WRITE_ARB));
966
            LEAVE_GL();
967 968 969 970 971 972
        }
    }
    else
    {
        This->flags |= WINED3D_BUFFER_DIRTY;
    }
973

974 975 976
    *data = This->resource.allocatedMemory + offset;

    TRACE("Returning memory at %p (base %p, offset %u)\n", *data, This->resource.allocatedMemory, offset);
977 978 979 980 981
    /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */

    return WINED3D_OK;
}

982
static HRESULT STDMETHODCALLTYPE buffer_Unmap(IWineD3DBuffer *iface)
983
{
984
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
985 986 987

    TRACE("(%p)\n", This);

988 989 990 991 992 993 994 995 996 997
    /* In the case that the number of Unmap calls > the
     * number of Map calls, d3d returns always D3D_OK.
     * This is also needed to prevent Map from returning garbage on
     * the next call (this will happen if the lock_count is < 0). */
    if(This->lock_count == 0)
    {
        TRACE("Unmap called without a previous Map call!\n");
        return WINED3D_OK;
    }

998
    if (InterlockedDecrement(&This->lock_count))
999 1000
    {
        /* Delay loading the buffer until everything is unlocked */
1001
        TRACE("Ignoring unlock\n");
1002 1003 1004
        return WINED3D_OK;
    }

1005 1006
    if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object)
    {
1007 1008
        IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;

1009 1010 1011 1012
        if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
        {
            IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER);
        }
1013 1014 1015

        ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
        ENTER_GL();
1016 1017
        GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
        GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint));
1018 1019
        LEAVE_GL();

1020 1021 1022
        This->resource.allocatedMemory = NULL;
    }
    else if (This->flags & WINED3D_BUFFER_HASDESC)
1023
    {
1024
        buffer_PreLoad(iface);
1025 1026 1027 1028 1029
    }

    return WINED3D_OK;
}

1030
static HRESULT STDMETHODCALLTYPE buffer_GetDesc(IWineD3DBuffer *iface, WINED3DBUFFER_DESC *desc)
1031
{
1032
    struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
1033 1034 1035

    TRACE("(%p)\n", This);

1036 1037 1038 1039
    desc->Type = This->resource.resourceType;
    desc->Usage = This->resource.usage;
    desc->Pool = This->resource.pool;
    desc->Size = This->resource.size;
1040 1041 1042 1043

    return WINED3D_OK;
}

1044
const struct IWineD3DBufferVtbl wined3d_buffer_vtbl =
1045 1046
{
    /* IUnknown methods */
1047 1048 1049
    buffer_QueryInterface,
    buffer_AddRef,
    buffer_Release,
1050
    /* IWineD3DBase methods */
1051
    buffer_GetParent,
1052
    /* IWineD3DResource methods */
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
    buffer_GetDevice,
    buffer_SetPrivateData,
    buffer_GetPrivateData,
    buffer_FreePrivateData,
    buffer_SetPriority,
    buffer_GetPriority,
    buffer_PreLoad,
    buffer_UnLoad,
    buffer_GetType,
    /* IWineD3DBuffer methods */
    buffer_Map,
    buffer_Unmap,
    buffer_GetDesc,
1066
};