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

#include "config.h"
Henri Verbeet's avatar
Henri Verbeet committed
28 29
#include "wine/port.h"

30 31
#include "wined3d_private.h"

32
WINE_DEFAULT_DEBUG_CHANNEL(d3d_draw);
33
WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
34

35
#include <stdio.h>
36
#include <math.h>
37

38
/* Context activation is done by the caller. */
39
static void drawStridedFast(const struct wined3d_gl_info *gl_info, GLenum primitive_type, UINT count, UINT idx_size,
40
        const void *idx_data, UINT start_idx, INT base_vertex_index, UINT start_instance, UINT instance_count)
41
{
42 43
    if (idx_size)
    {
44
        GLenum idxtype = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
45 46
        if (instance_count)
        {
47 48 49
            if (start_instance)
                FIXME("Start instance (%u) not supported.\n", start_instance);
            if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
50
            {
51 52 53
                GL_EXTCALL(glDrawElementsInstancedBaseVertex(primitive_type, count, idxtype,
                        (const char *)idx_data + (idx_size * start_idx), instance_count, base_vertex_index));
                checkGLcall("glDrawElementsInstancedBaseVertex");
54 55 56
            }
            else
            {
57 58 59
                GL_EXTCALL(glDrawElementsInstanced(primitive_type, count, idxtype,
                        (const char *)idx_data + (idx_size * start_idx), instance_count));
                checkGLcall("glDrawElementsInstanced");
60 61 62
            }
        }
        else if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
63 64 65 66 67 68 69
        {
            GL_EXTCALL(glDrawElementsBaseVertex(primitive_type, count, idxtype,
                    (const char *)idx_data + (idx_size * start_idx), base_vertex_index));
            checkGLcall("glDrawElementsBaseVertex");
        }
        else
        {
70
            gl_info->gl_ops.gl.p_glDrawElements(primitive_type, count,
71 72 73
                    idxtype, (const char *)idx_data + (idx_size * start_idx));
            checkGLcall("glDrawElements");
        }
74 75 76
    }
    else
    {
77 78 79 80 81 82 83 84 85 86 87 88
        if (instance_count)
        {
            if (start_instance)
                FIXME("Start instance (%u) not supported.\n", start_instance);
            GL_EXTCALL(glDrawArraysInstanced(primitive_type, start_idx, count, instance_count));
            checkGLcall("glDrawArraysInstanced");
        }
        else
        {
            gl_info->gl_ops.gl.p_glDrawArrays(primitive_type, start_idx, count);
            checkGLcall("glDrawArrays");
        }
89 90 91
    }
}

92
/*
93 94 95
 * Actually draw using the supplied information.
 * Slower GL version which extracts info about each vertex in turn
 */
96

97
/* Context activation is done by the caller. */
98
static void drawStridedSlow(const struct wined3d_device *device, struct wined3d_context *context,
99
        const struct wined3d_stream_info *si, UINT NumVertexes, GLenum glPrimType,
100
        const void *idxData, UINT idxSize, UINT startIdx)
101
{
102
    unsigned int               textureNo;
103 104
    const WORD                *pIdxBufS     = NULL;
    const DWORD               *pIdxBufL     = NULL;
105
    UINT vx_index;
106
    const struct wined3d_state *state = &device->state;
107
    LONG SkipnStrides = startIdx;
108
    BOOL pixelShader = use_ps(state);
109
    BOOL specular_fog = FALSE;
110 111
    const BYTE *texCoords[WINED3DDP_MAXTEXCOORD];
    const BYTE *diffuse = NULL, *specular = NULL, *normal = NULL, *position = NULL;
112
    const struct wined3d_gl_info *gl_info = context->gl_info;
113
    const struct wined3d_d3d_info *d3d_info = context->d3d_info;
114
    const struct wined3d_ffp_attrib_ops *ops = &d3d_info->ffp_attrib_ops;
115
    UINT texture_stages = d3d_info->limits.ffp_blend_stages;
116
    const struct wined3d_stream_info_element *element;
117
    UINT num_untracked_materials;
118
    DWORD tex_mask = 0;
119

120
    TRACE_(d3d_perf)("Using slow vertex array code\n");
121 122

    /* Variable Initialization */
123 124 125 126 127 128 129
    if (idxSize)
    {
        /* Immediate mode drawing can't make use of indices in a vbo - get the
         * data from the index buffer. If the index buffer has no vbo (not
         * supported or other reason), or with user pointer drawing idxData
         * will be non-NULL. */
        if (!idxData)
130
            idxData = buffer_get_sysmem(state->index_buffer, context);
131

132 133
        if (idxSize == 2) pIdxBufS = idxData;
        else pIdxBufL = idxData;
134 135 136
    } else if (idxData) {
        ERR("non-NULL idxData with 0 idxSize, this should never happen\n");
        return;
137 138
    }

139
    /* Start drawing in GL */
140
    gl_info->gl_ops.gl.p_glBegin(glPrimType);
141

142
    if (si->use_map & (1u << WINED3D_FFP_POSITION))
143 144
    {
        element = &si->elements[WINED3D_FFP_POSITION];
145
        position = element->data.addr;
146
    }
147

148
    if (si->use_map & (1u << WINED3D_FFP_NORMAL))
149 150
    {
        element = &si->elements[WINED3D_FFP_NORMAL];
151
        normal = element->data.addr;
152 153 154
    }
    else
    {
155
        gl_info->gl_ops.gl.p_glNormal3f(0, 0, 0);
156
    }
157

158
    num_untracked_materials = context->num_untracked_materials;
159
    if (si->use_map & (1u << WINED3D_FFP_DIFFUSE))
160 161
    {
        element = &si->elements[WINED3D_FFP_DIFFUSE];
162
        diffuse = element->data.addr;
163

164 165
        if (num_untracked_materials && element->format->id != WINED3DFMT_B8G8R8A8_UNORM)
            FIXME("Implement diffuse color tracking from %s\n", debug_d3dformat(element->format->id));
166 167 168
    }
    else
    {
169
        gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
170
    }
171

172
    if (si->use_map & (1u << WINED3D_FFP_SPECULAR))
173
    {
174
        element = &si->elements[WINED3D_FFP_SPECULAR];
175
        specular = element->data.addr;
176

177
        /* special case where the fog density is stored in the specular alpha channel */
178
        if (state->render_states[WINED3D_RS_FOGENABLE]
179
                && (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE
180
                    || si->elements[WINED3D_FFP_POSITION].format->id == WINED3DFMT_R32G32B32A32_FLOAT)
181
                && state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE)
182
        {
183
            if (gl_info->supported[EXT_FOG_COORD])
184
            {
185 186
                if (element->format->id == WINED3DFMT_B8G8R8A8_UNORM) specular_fog = TRUE;
                else FIXME("Implement fog coordinates from %s\n", debug_d3dformat(element->format->id));
187 188 189 190 191 192 193 194 195 196 197
            }
            else
            {
                static BOOL warned;

                if (!warned)
                {
                    /* TODO: Use the fog table code from old ddraw */
                    FIXME("Implement fog for transformed vertices in software\n");
                    warned = TRUE;
                }
198 199
            }
        }
200
    }
201
    else if (gl_info->supported[EXT_SECONDARY_COLOR])
202 203
    {
        GL_EXTCALL(glSecondaryColor3fEXT)(0, 0, 0);
204 205
    }

206 207
    for (textureNo = 0; textureNo < texture_stages; ++textureNo)
    {
208
        int coordIdx = state->texture_states[textureNo][WINED3D_TSS_TEXCOORD_INDEX];
209
        DWORD texture_idx = context->tex_unit_map[textureNo];
210

211
        if (!gl_info->supported[ARB_MULTITEXTURE] && textureNo > 0)
212 213 214 215 216
        {
            FIXME("Program using multiple concurrent textures which this opengl implementation doesn't support\n");
            continue;
        }

217
        if (!pixelShader && !state->textures[textureNo]) continue;
218

219
        if (texture_idx == WINED3D_UNMAPPED_STAGE) continue;
220 221 222 223 224 225 226 227 228 229 230 231

        if (coordIdx > 7)
        {
            TRACE("tex: %d - Skip tex coords, as being system generated\n", textureNo);
            continue;
        }
        else if (coordIdx < 0)
        {
            FIXME("tex: %d - Coord index %d is less than zero, expect a crash.\n", textureNo, coordIdx);
            continue;
        }

232
        if (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coordIdx)))
233
        {
234
            element = &si->elements[WINED3D_FFP_TEXCOORD0 + coordIdx];
235
            texCoords[coordIdx] = element->data.addr;
236
            tex_mask |= (1u << textureNo);
237 238 239 240
        }
        else
        {
            TRACE("tex: %d - Skipping tex coords, as no data supplied\n", textureNo);
241
            if (gl_info->supported[ARB_MULTITEXTURE])
242 243
                GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_idx, 0, 0, 0, 1));
            else
244
                gl_info->gl_ops.gl.p_glTexCoord4f(0, 0, 0, 1);
245 246 247
        }
    }

248 249 250 251
    /* We shouldn't start this function if any VBO is involved. Should I put a safety check here?
     * Guess it's not necessary(we crash then anyway) and would only eat CPU time
     */

252
    /* For each primitive */
253
    for (vx_index = 0; vx_index < NumVertexes; ++vx_index) {
254
        UINT texture, tmp_tex_mask;
255 256 257 258
        /* Blending data and Point sizes are not supported by this function. They are not supported by the fixed
         * function pipeline at all. A Fixme for them is printed after decoding the vertex declaration
         */

259
        /* For indexed data, we need to go a few more strides in */
260 261
        if (idxData)
        {
262
            /* Indexed so work out the number of strides to skip */
Henri Verbeet's avatar
Henri Verbeet committed
263
            if (idxSize == 2)
264
                SkipnStrides = pIdxBufS[startIdx + vx_index] + state->base_vertex_index;
Henri Verbeet's avatar
Henri Verbeet committed
265
            else
266
                SkipnStrides = pIdxBufL[startIdx + vx_index] + state->base_vertex_index;
267 268
        }

269 270 271 272 273
        tmp_tex_mask = tex_mask;
        for (texture = 0; tmp_tex_mask; tmp_tex_mask >>= 1, ++texture)
        {
            int coord_idx;
            const void *ptr;
274
            DWORD texture_idx;
275

276
            if (!(tmp_tex_mask & 1)) continue;
277

278
            coord_idx = state->texture_states[texture][WINED3D_TSS_TEXCOORD_INDEX];
279
            ptr = texCoords[coord_idx] + (SkipnStrides * si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].stride);
280

281
            texture_idx = context->tex_unit_map[texture];
282
            ops->texcoord[si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->emit_idx](
283
                    GL_TEXTURE0_ARB + texture_idx, ptr);
284
        }
285 286

        /* Diffuse -------------------------------- */
287 288
        if (diffuse)
        {
289
            const void *ptrToCoords = diffuse + SkipnStrides * si->elements[WINED3D_FFP_DIFFUSE].stride;
290
            ops->diffuse[si->elements[WINED3D_FFP_DIFFUSE].format->emit_idx](ptrToCoords);
291

292 293
            if (num_untracked_materials)
            {
294
                DWORD diffuseColor = ((const DWORD *)ptrToCoords)[0];
295 296
                unsigned char i;
                float color[4];
297

298 299 300 301
                color[0] = D3DCOLOR_B_R(diffuseColor) / 255.0f;
                color[1] = D3DCOLOR_B_G(diffuseColor) / 255.0f;
                color[2] = D3DCOLOR_B_B(diffuseColor) / 255.0f;
                color[3] = D3DCOLOR_B_A(diffuseColor) / 255.0f;
302

303 304
                for (i = 0; i < num_untracked_materials; ++i)
                {
305
                    gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, context->untracked_materials[i], color);
306 307
                }
            }
308 309 310
        }

        /* Specular ------------------------------- */
311 312
        if (specular)
        {
313
            const void *ptrToCoords = specular + SkipnStrides * si->elements[WINED3D_FFP_SPECULAR].stride;
314
            ops->specular[si->elements[WINED3D_FFP_SPECULAR].format->emit_idx](ptrToCoords);
315 316 317 318

            if (specular_fog)
            {
                DWORD specularColor = *(const DWORD *)ptrToCoords;
319
                GL_EXTCALL(glFogCoordfEXT((float) (specularColor >> 24)));
320
            }
321 322 323
        }

        /* Normal -------------------------------- */
324 325
        if (normal)
        {
326
            const void *ptrToCoords = normal + SkipnStrides * si->elements[WINED3D_FFP_NORMAL].stride;
327
            ops->normal[si->elements[WINED3D_FFP_NORMAL].format->emit_idx](ptrToCoords);
328
        }
329

330
        /* Position -------------------------------- */
331 332
        if (position)
        {
333
            const void *ptrToCoords = position + SkipnStrides * si->elements[WINED3D_FFP_POSITION].stride;
334
            ops->position[si->elements[WINED3D_FFP_POSITION].format->emit_idx](ptrToCoords);
335 336 337
        }

        /* For non indexed mode, step onto next parts */
338
        if (!idxData) ++SkipnStrides;
339 340
    }

341
    gl_info->gl_ops.gl.p_glEnd();
342 343 344
    checkGLcall("glEnd and previous calls");
}

345
/* Context activation is done by the caller. */
346
static inline void send_attribute(const struct wined3d_gl_info *gl_info,
347
        enum wined3d_format_id format, const UINT index, const void *ptr)
348 349 350 351
{
    switch(format)
    {
        case WINED3DFMT_R32_FLOAT:
352
            GL_EXTCALL(glVertexAttrib1fv(index, ptr));
353
            break;
354
        case WINED3DFMT_R32G32_FLOAT:
355
            GL_EXTCALL(glVertexAttrib2fv(index, ptr));
356
            break;
357
        case WINED3DFMT_R32G32B32_FLOAT:
358
            GL_EXTCALL(glVertexAttrib3fv(index, ptr));
359
            break;
360
        case WINED3DFMT_R32G32B32A32_FLOAT:
361
            GL_EXTCALL(glVertexAttrib4fv(index, ptr));
362 363
            break;

364
        case WINED3DFMT_R8G8B8A8_UINT:
365
            GL_EXTCALL(glVertexAttrib4ubv(index, ptr));
366
            break;
367
        case WINED3DFMT_B8G8R8A8_UNORM:
368
            if (gl_info->supported[ARB_VERTEX_ARRAY_BGRA])
369 370
            {
                const DWORD *src = ptr;
371 372 373
                DWORD c = *src & 0xff00ff00u;
                c |= (*src & 0xff0000u) >> 16;
                c |= (*src & 0xffu) << 16;
374
                GL_EXTCALL(glVertexAttrib4Nubv(index, (GLubyte *)&c));
375 376 377
                break;
            }
            /* else fallthrough */
378
        case WINED3DFMT_R8G8B8A8_UNORM:
379
            GL_EXTCALL(glVertexAttrib4Nubv(index, ptr));
380 381
            break;

382
        case WINED3DFMT_R16G16_SINT:
383
            GL_EXTCALL(glVertexAttrib2sv(index, ptr));
384
            break;
385
        case WINED3DFMT_R16G16B16A16_SINT:
386
            GL_EXTCALL(glVertexAttrib4sv(index, ptr));
387 388
            break;

389
        case WINED3DFMT_R16G16_SNORM:
390
        {
391
            GLshort s[4] = {((const GLshort *)ptr)[0], ((const GLshort *)ptr)[1], 0, 1};
392
            GL_EXTCALL(glVertexAttrib4Nsv(index, s));
393 394
            break;
        }
395
        case WINED3DFMT_R16G16_UNORM:
396
        {
397
            GLushort s[4] = {((const GLushort *)ptr)[0], ((const GLushort *)ptr)[1], 0, 1};
398
            GL_EXTCALL(glVertexAttrib4Nusv(index, s));
399 400
            break;
        }
401
        case WINED3DFMT_R16G16B16A16_SNORM:
402
            GL_EXTCALL(glVertexAttrib4Nsv(index, ptr));
403
            break;
404
        case WINED3DFMT_R16G16B16A16_UNORM:
405
            GL_EXTCALL(glVertexAttrib4Nusv(index, ptr));
406 407
            break;

408
        case WINED3DFMT_R10G10B10A2_UINT:
409 410 411
            FIXME("Unsure about WINED3DDECLTYPE_UDEC3\n");
            /*glVertexAttrib3usvARB(instancedData[j], (GLushort *) ptr); Does not exist */
            break;
412
        case WINED3DFMT_R10G10B10A2_SNORM:
413 414 415 416
            FIXME("Unsure about WINED3DDECLTYPE_DEC3N\n");
            /*glVertexAttrib3NusvARB(instancedData[j], (GLushort *) ptr); Does not exist */
            break;

417
        case WINED3DFMT_R16G16_FLOAT:
418 419 420
            /* Are those 16 bit floats. C doesn't have a 16 bit float type. I could read the single bits and calculate a 4
             * byte float according to the IEEE standard
             */
421
            if (gl_info->supported[NV_HALF_FLOAT] && gl_info->supported[NV_VERTEX_PROGRAM])
422
            {
423
                /* Not supported by GL_ARB_half_float_vertex */
424
                GL_EXTCALL(glVertexAttrib2hvNV(index, ptr));
425 426 427
            }
            else
            {
428 429
                float x = float_16_to_32(((const unsigned short *)ptr) + 0);
                float y = float_16_to_32(((const unsigned short *)ptr) + 1);
430
                GL_EXTCALL(glVertexAttrib2f(index, x, y));
431 432
            }
            break;
433
        case WINED3DFMT_R16G16B16A16_FLOAT:
434
            if (gl_info->supported[NV_HALF_FLOAT] && gl_info->supported[NV_VERTEX_PROGRAM])
435
            {
436
                /* Not supported by GL_ARB_half_float_vertex */
437
                GL_EXTCALL(glVertexAttrib4hvNV(index, ptr));
438 439 440
            }
            else
            {
441 442 443 444
                float x = float_16_to_32(((const unsigned short *)ptr) + 0);
                float y = float_16_to_32(((const unsigned short *)ptr) + 1);
                float z = float_16_to_32(((const unsigned short *)ptr) + 2);
                float w = float_16_to_32(((const unsigned short *)ptr) + 3);
445
                GL_EXTCALL(glVertexAttrib4f(index, x, y, z, w));
446 447 448 449
            }
            break;

        default:
450
            ERR("Unexpected attribute format: %s\n", debug_d3dformat(format));
451 452 453 454
            break;
    }
}

455
/* Context activation is done by the caller. */
456
static void drawStridedSlowVs(struct wined3d_context *context, const struct wined3d_state *state,
457 458
        const struct wined3d_stream_info *si, UINT numberOfVertices, GLenum glPrimitiveType,
        const void *idxData, UINT idxSize, UINT startIdx)
459
{
460
    const struct wined3d_gl_info *gl_info = context->gl_info;
461
    LONG SkipnStrides = startIdx + state->load_base_vertex_index;
462 463
    const DWORD *pIdxBufL = NULL;
    const WORD *pIdxBufS = NULL;
464
    UINT vx_index;
465
    int i;
466
    const BYTE *ptr;
467

468 469 470 471 472 473 474
    if (idxSize)
    {
        /* Immediate mode drawing can't make use of indices in a vbo - get the
         * data from the index buffer. If the index buffer has no vbo (not
         * supported or other reason), or with user pointer drawing idxData
         * will be non-NULL. */
        if (!idxData)
475
            idxData = buffer_get_sysmem(state->index_buffer, context);
476

477 478
        if (idxSize == 2) pIdxBufS = idxData;
        else pIdxBufL = idxData;
479 480 481
    } else if (idxData) {
        ERR("non-NULL idxData with 0 idxSize, this should never happen\n");
        return;
482 483 484
    }

    /* Start drawing in GL */
485
    gl_info->gl_ops.gl.p_glBegin(glPrimitiveType);
486

487 488 489 490
    for (vx_index = 0; vx_index < numberOfVertices; ++vx_index)
    {
        if (idxData)
        {
491
            /* Indexed so work out the number of strides to skip */
Henri Verbeet's avatar
Henri Verbeet committed
492
            if (idxSize == 2)
493
                SkipnStrides = pIdxBufS[startIdx + vx_index] + state->load_base_vertex_index;
Henri Verbeet's avatar
Henri Verbeet committed
494
            else
495
                SkipnStrides = pIdxBufL[startIdx + vx_index] + state->load_base_vertex_index;
496 497
        }

498 499
        for (i = MAX_ATTRIBS - 1; i >= 0; i--)
        {
500
            if (!(si->use_map & (1u << i))) continue;
501

502
            ptr = si->elements[i].data.addr + si->elements[i].stride * SkipnStrides;
503

504
            send_attribute(gl_info, si->elements[i].format->id, i, ptr);
505
        }
506
        SkipnStrides++;
507 508
    }

509
    gl_info->gl_ops.gl.p_glEnd();
510 511
}

512
/* Context activation is done by the caller. */
513
static void drawStridedInstanced(struct wined3d_context *context, const struct wined3d_state *state,
514
        const struct wined3d_stream_info *si, UINT numberOfVertices, GLenum glPrimitiveType,
515
        const void *idxData, UINT idxSize, UINT startIdx, UINT base_vertex_index, UINT instance_count)
516
{
517
    const struct wined3d_gl_info *gl_info = context->gl_info;
518
    int numInstancedAttribs = 0, j;
519
    UINT instancedData[sizeof(si->elements) / sizeof(*si->elements) /* 16 */];
520
    GLenum idxtype = idxSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
521
    UINT i;
522

523 524
    if (!idxSize)
    {
525 526 527 528 529 530 531 532 533 534
        /* This is a nasty thing. MSDN says no hardware supports that and apps have to use software vertex processing.
         * We don't support this for now
         *
         * Shouldn't be too hard to support with opengl, in theory just call glDrawArrays instead of drawElements.
         * But the StreamSourceFreq value has a different meaning in that situation.
         */
        FIXME("Non-indexed instanced drawing is not supported\n");
        return;
    }

535 536
    for (i = 0; i < sizeof(si->elements) / sizeof(*si->elements); ++i)
    {
537
        if (!(si->use_map & (1u << i))) continue;
538

539
        if (state->streams[si->elements[i].stream_idx].flags & WINED3DSTREAMSOURCE_INSTANCEDATA)
540
        {
541 542 543 544 545
            instancedData[numInstancedAttribs] = i;
            numInstancedAttribs++;
        }
    }

546 547
    for (i = 0; i < instance_count; ++i)
    {
548 549
        /* Specify the instanced attributes using immediate mode calls */
        for(j = 0; j < numInstancedAttribs; j++) {
550
            const BYTE *ptr = si->elements[instancedData[j]].data.addr
551
                    + si->elements[instancedData[j]].stride * i;
552
            if (si->elements[instancedData[j]].data.buffer_object)
553
            {
554
                struct wined3d_buffer *vb = state->streams[si->elements[instancedData[j]].stream_idx].buffer;
555
                ptr += (ULONG_PTR)buffer_get_sysmem(vb, context);
556 557
            }

558
            send_attribute(gl_info, si->elements[instancedData[j]].format->id, instancedData[j], ptr);
559 560
        }

561 562 563 564 565 566 567 568
        if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
        {
            GL_EXTCALL(glDrawElementsBaseVertex(glPrimitiveType, numberOfVertices, idxtype,
                        (const char *)idxData+(idxSize * startIdx), base_vertex_index));
            checkGLcall("glDrawElementsBaseVertex");
        }
        else
        {
569 570
            gl_info->gl_ops.gl.p_glDrawElements(glPrimitiveType, numberOfVertices, idxtype,
                        (const char *)idxData + (idxSize * startIdx));
571 572
            checkGLcall("glDrawElements");
        }
573 574 575
    }
}

576
static void remove_vbos(struct wined3d_context *context,
577
        const struct wined3d_state *state, struct wined3d_stream_info *s)
578
{
579
    unsigned int i;
580

581
    for (i = 0; i < (sizeof(s->elements) / sizeof(*s->elements)); ++i)
582
    {
583 584
        struct wined3d_stream_info_element *e;

585
        if (!(s->use_map & (1u << i))) continue;
586 587

        e = &s->elements[i];
588
        if (e->data.buffer_object)
589
        {
590
            struct wined3d_buffer *vb = state->streams[e->stream_idx].buffer;
591
            e->data.buffer_object = 0;
592
            e->data.addr = (BYTE *)((ULONG_PTR)e->data.addr + (ULONG_PTR)buffer_get_sysmem(vb, context));
593 594 595 596
        }
    }
}

597
/* Routine common to the draw primitive and draw indexed primitive routines */
598
void draw_primitive(struct wined3d_device *device, UINT start_idx, UINT index_count,
599
        UINT start_instance, UINT instance_count, BOOL indexed)
600
{
601
    const struct wined3d_state *state = &device->state;
602
    const struct wined3d_stream_info *stream_info;
603
    struct wined3d_event_query *ib_query = NULL;
604
    struct wined3d_stream_info si_emulated;
605
    const struct wined3d_gl_info *gl_info;
606
    struct wined3d_context *context;
607
    BOOL emulation = FALSE;
608
    const void *idx_data = NULL;
609
    UINT idx_size = 0;
610
    unsigned int i;
611

612
    if (!index_count) return;
613

614 615 616 617 618 619 620 621 622
    context = context_acquire(device, wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]));
    if (!context->valid)
    {
        context_release(context);
        WARN("Invalid context, skipping draw.\n");
        return;
    }
    gl_info = context->gl_info;

623
    for (i = 0; i < device->adapter->gl_info.limits.buffers; ++i)
624
    {
625 626
        struct wined3d_surface *target = wined3d_rendertarget_view_get_surface(device->fb.render_targets[i]);
        if (target && target->resource.format->id != WINED3DFMT_NULL)
627
        {
628
            if (state->render_states[WINED3D_RS_COLORWRITEENABLE])
629
            {
630
                surface_load_location(target, context, target->container->resource.draw_binding);
631
                surface_invalidate_location(target, ~target->container->resource.draw_binding);
632
            }
633 634 635 636
            else
            {
                wined3d_surface_prepare(target, context, target->container->resource.draw_binding);
            }
637 638 639
        }
    }

640
    if (device->fb.depth_stencil)
641
    {
642
        /* Note that this depends on the context_acquire() call above to set
643
         * context->render_offscreen properly. We don't currently take the
644 645 646
         * Z-compare function into account, but we could skip loading the
         * depthstencil for D3DCMP_NEVER and D3DCMP_ALWAYS as well. Also note
         * that we never copy the stencil data.*/
647
        DWORD location = context->render_offscreen ? device->fb.depth_stencil->resource->draw_binding
648
                : WINED3D_LOCATION_DRAWABLE;
649 650
        struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(device->fb.depth_stencil);

651
        if (state->render_states[WINED3D_RS_ZWRITEENABLE] || state->render_states[WINED3D_RS_ZENABLE])
652
        {
653 654
            RECT current_rect, draw_rect, r;

655
            if (!context->render_offscreen && ds != device->onscreen_depth_stencil)
656
                device_switch_onscreen_ds(device, context, ds);
657

658
            if (ds->locations & location)
659
                SetRect(&current_rect, 0, 0, ds->ds_current_size.cx, ds->ds_current_size.cy);
660 661 662
            else
                SetRectEmpty(&current_rect);

663
            wined3d_get_draw_rect(state, &draw_rect);
664 665 666

            IntersectRect(&r, &draw_rect, &current_rect);
            if (!EqualRect(&r, &draw_rect))
667
                surface_load_ds_location(ds, context, location);
668 669
            else
                wined3d_surface_prepare(ds, context, location);
670
        }
671 672
        else
            wined3d_surface_prepare(ds, context, location);
673 674
    }

675 676 677 678 679 680 681
    if (!context_apply_draw_state(context, device))
    {
        context_release(context);
        WARN("Unable to apply draw state, skipping draw.\n");
        return;
    }

682 683
    if (device->fb.depth_stencil && state->render_states[WINED3D_RS_ZWRITEENABLE])
    {
684
        struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(device->fb.depth_stencil);
685
        DWORD location = context->render_offscreen ? ds->container->resource.draw_binding : WINED3D_LOCATION_DRAWABLE;
686 687 688 689

        surface_modify_ds_location(ds, location, ds->ds_current_size.cx, ds->ds_current_size.cy);
    }

690 691
    if ((!gl_info->supported[WINED3D_GL_VERSION_2_0]
            || !gl_info->supported[NV_POINT_SPRITE])
692
            && context->render_offscreen
693
            && state->render_states[WINED3D_RS_POINTSPRITEENABLE]
694
            && state->gl_primitive_type == GL_POINTS)
695 696 697 698
    {
        FIXME("Point sprite coordinate origin switching not supported.\n");
    }

699
    stream_info = &context->stream_info;
700 701
    if (context->instance_count)
        instance_count = context->instance_count;
702

703 704
    if (indexed)
    {
705 706
        struct wined3d_buffer *index_buffer = state->index_buffer;
        if (!index_buffer->buffer_object || !stream_info->all_vbo)
707
            idx_data = index_buffer->resource.heap_memory;
708
        else
709
        {
710 711
            ib_query = index_buffer->query;
            idx_data = NULL;
712 713 714 715 716 717 718
        }

        if (state->index_format == WINED3DFMT_R16_UINT)
            idx_size = 2;
        else
            idx_size = 4;
    }
719

720 721 722 723 724 725 726 727 728
    if (!use_vs(state))
    {
        if (!stream_info->position_transformed && context->num_untracked_materials
                && state->render_states[WINED3D_RS_LIGHTING])
        {
            static BOOL warned;

            if (!warned++)
                FIXME("Using software emulation because not all material properties could be tracked.\n");
729
            else
730
                WARN_(d3d_perf)("Using software emulation because not all material properties could be tracked.\n");
731
            emulation = TRUE;
732
        }
733
        else if (context->fog_coord && state->render_states[WINED3D_RS_FOGENABLE])
734
        {
735
            static BOOL warned;
736

737 738 739 740 741 742
            /* Either write a pipeline replacement shader or convert the
             * specular alpha from unsigned byte to a float in the vertex
             * buffer. */
            if (!warned++)
                FIXME("Using software emulation because manual fog coordinates are provided.\n");
            else
743
                WARN_(d3d_perf)("Using software emulation because manual fog coordinates are provided.\n");
744
            emulation = TRUE;
745 746
        }

747
        if (emulation)
748
        {
749
            si_emulated = context->stream_info;
750
            remove_vbos(context, state, &si_emulated);
751
            stream_info = &si_emulated;
752
        }
753 754
    }

755
    if (context->use_immediate_mode_draw || emulation)
756 757 758
    {
        /* Immediate mode drawing. */
        if (use_vs(state))
759
        {
760 761 762 763 764
            static BOOL warned;

            if (!warned++)
                FIXME("Using immediate mode with vertex shaders for half float emulation.\n");
            else
765
                WARN_(d3d_perf)("Using immediate mode with vertex shaders for half float emulation.\n");
766

767
            drawStridedSlowVs(context, state, stream_info, index_count,
768
                    state->gl_primitive_type, idx_data, idx_size, start_idx);
769 770 771
        }
        else
        {
772 773
            if (context->d3d_info->ffp_generic_attributes)
                drawStridedSlowVs(context, state, stream_info, index_count,
774
                    state->gl_primitive_type, idx_data, idx_size, start_idx);
775 776 777
            else
                drawStridedSlow(device, context, stream_info, index_count,
                        state->gl_primitive_type, idx_data, idx_size, start_idx);
778
        }
779
    }
780 781 782
    else if (!gl_info->supported[ARB_INSTANCED_ARRAYS] && instance_count)
    {
        /* Instancing emulation by mixing immediate mode and arrays. */
783
        drawStridedInstanced(context, state, stream_info, index_count, state->gl_primitive_type,
784 785 786 787 788 789 790
                idx_data, idx_size, start_idx, state->base_vertex_index, instance_count);
    }
    else
    {
        drawStridedFast(gl_info, state->gl_primitive_type, index_count, idx_size, idx_data,
                start_idx, state->base_vertex_index, start_instance, instance_count);
    }
791

792 793
    if (ib_query)
        wined3d_event_query_issue(ib_query, device);
794
    for (i = 0; i < context->num_buffer_queries; ++i)
795
    {
796
        wined3d_event_query_issue(context->buffer_queries[i], device);
797 798
    }

799 800
    if (wined3d_settings.strict_draw_ordering)
        gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
801

802 803
    context_release(context);

804 805
    TRACE("Done all gl drawing\n");
}