source: trunk/src/gpu/vertexbuffer.cpp @ 1864

Last change on this file since 1864 was 1864, checked in by sam, 8 years ago

gpu: do not try to call glVertexAttribIPointer if Glew did not find it.

File size: 19.1 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include "core.h"
16#include "lolgl.h"
17
18#if defined _WIN32 && defined USE_D3D9
19#   define FAR
20#   define NEAR
21#   include <d3d9.h>
22#endif
23
24using namespace std;
25
26#if defined USE_D3D9
27extern IDirect3DDevice9 *g_d3ddevice;
28#elif defined _XBOX
29extern D3DDevice *g_d3ddevice;
30#endif
31
32namespace lol
33{
34
35//
36// The VertexBufferData class
37// --------------------------
38//
39
40class VertexBufferData
41{
42    friend class VertexBuffer;
43    friend class VertexDeclaration;
44
45    size_t m_size;
46
47#if defined USE_D3D9
48    IDirect3DVertexBuffer9 *m_vbo;
49#elif defined _XBOX
50    D3DVertexBuffer *m_vbo;
51#else
52    GLuint m_vbo;
53    uint8_t *m_memory;
54#endif
55};
56
57//
58// The VertexDeclaration class
59// ---------------------------
60//
61
62VertexStreamBase const VertexStreamBase::Empty;
63
64VertexDeclaration::VertexDeclaration(VertexStreamBase const &s1,
65                                     VertexStreamBase const &s2,
66                                     VertexStreamBase const &s3,
67                                     VertexStreamBase const &s4,
68                                     VertexStreamBase const &s5,
69                                     VertexStreamBase const &s6,
70                                     VertexStreamBase const &s7,
71                                     VertexStreamBase const &s8,
72                                     VertexStreamBase const &s9,
73                                     VertexStreamBase const &s10,
74                                     VertexStreamBase const &s11,
75                                     VertexStreamBase const &s12) : m_count(0)
76{
77    if (&s1 != &VertexStreamBase::Empty) AddStream(s1);
78    if (&s2 != &VertexStreamBase::Empty) AddStream(s2);
79    if (&s3 != &VertexStreamBase::Empty) AddStream(s3);
80    if (&s4 != &VertexStreamBase::Empty) AddStream(s4);
81    if (&s5 != &VertexStreamBase::Empty) AddStream(s5);
82    if (&s6 != &VertexStreamBase::Empty) AddStream(s6);
83    if (&s7 != &VertexStreamBase::Empty) AddStream(s7);
84    if (&s8 != &VertexStreamBase::Empty) AddStream(s8);
85    if (&s9 != &VertexStreamBase::Empty) AddStream(s9);
86    if (&s10 != &VertexStreamBase::Empty) AddStream(s10);
87    if (&s11 != &VertexStreamBase::Empty) AddStream(s11);
88    if (&s12 != &VertexStreamBase::Empty) AddStream(s12);
89    Initialize();
90}
91
92VertexDeclaration::~VertexDeclaration()
93{
94#if defined _XBOX || defined USE_D3D9
95#   if defined USE_D3D9
96    IDirect3DVertexDeclaration9 *vdecl = (IDirect3DVertexDeclaration9 *)m_data;
97#   elif defined _XBOX
98    D3DVertexDeclaration *vdecl = (D3DVertexDeclaration *)m_data;
99#   endif
100
101    if (FAILED(vdecl->Release()))
102        Abort();
103#else
104
105#endif
106}
107
108void VertexDeclaration::Bind()
109{
110#if defined _XBOX || defined USE_D3D9
111#   if defined USE_D3D9
112    IDirect3DVertexDeclaration9 *vdecl = (IDirect3DVertexDeclaration9 *)m_data;
113#   elif defined _XBOX
114    D3DVertexDeclaration *vdecl = (D3DVertexDeclaration *)m_data;
115#   endif
116
117    if (FAILED(g_d3ddevice->SetVertexDeclaration(vdecl)))
118        Abort();
119#else
120    /* FIXME: Nothing to do? */
121#endif
122}
123
124void VertexDeclaration::DrawElements(MeshPrimitive type, int skip, int count)
125{
126    if (count <= 0)
127        return;
128
129#if defined _XBOX || defined USE_D3D9
130    g_d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
131    g_d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
132    g_d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
133    if (FAILED(g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW)))
134        Abort();
135    switch (type)
136    {
137    case MeshPrimitive::Triangles:
138        if (FAILED(g_d3ddevice->DrawPrimitive(D3DPT_TRIANGLELIST, skip, count)))
139            Abort();
140        break;
141    case MeshPrimitive::Points:
142        if (FAILED(g_d3ddevice->DrawPrimitive(D3DPT_POINTLIST, skip, count)))
143            Abort();
144        break;
145    }
146#else
147    glFrontFace(GL_CCW);
148    glEnable(GL_CULL_FACE);
149#   if defined HAVE_GL_2X && !defined __APPLE__
150    /* FIXME: this has nothing to do here! */
151    glEnable(GL_ALPHA_TEST);
152    glAlphaFunc(GL_GEQUAL, 0.01f);
153#   endif
154    glEnable(GL_BLEND);
155    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
156
157    switch (type)
158    {
159    case MeshPrimitive::Triangles:
160        glDrawArrays(GL_TRIANGLES, skip * 3, count * 3);
161        break;
162    case MeshPrimitive::Points:
163        glDrawArrays(GL_POINTS, skip, count);
164        break;
165    }
166#   if defined HAVE_GL_2X && !defined __APPLE__
167    /* FIXME: this has nothing to do here! */
168    glDisable(GL_ALPHA_TEST);
169#   endif
170#endif
171}
172
173void VertexDeclaration::DrawIndexedElements(MeshPrimitive type, int vbase,
174                                            int vskip, int vcount,
175                                            int skip, int count)
176{
177    if (count <= 0)
178        return;
179
180#if defined _XBOX || defined USE_D3D9
181    g_d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
182    g_d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
183    g_d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
184    g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
185    switch (type)
186    {
187    case MeshPrimitive::Triangles:
188        if (FAILED(g_d3ddevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vbase, vskip, vcount, skip, count)))
189            Abort();
190        break;
191    case MeshPrimitive::Points:
192        if (FAILED(g_d3ddevice->DrawIndexedPrimitive(D3DPT_POINTLIST, vbase, vskip, vcount, skip, count)))
193            Abort();
194        break;
195    }
196#else
197    glFrontFace(GL_CCW);
198    glEnable(GL_CULL_FACE);
199#   if defined HAVE_GL_2X && !defined __APPLE__
200    /* FIXME: this has nothing to do here! */
201    glEnable(GL_ALPHA_TEST);
202    glAlphaFunc(GL_GEQUAL, 0.01f);
203#   endif
204    glEnable(GL_BLEND);
205    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
206
207    switch (type)
208    {
209    case MeshPrimitive::Triangles:
210        /* FIXME: ignores most of the arguments! */
211        glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, 0);
212        break;
213    case MeshPrimitive::Points:
214        /* FIXME: ignores most of the arguments! */
215        glDrawElements(GL_POINTS, count, GL_UNSIGNED_SHORT, 0);
216        break;
217    }
218#   if defined HAVE_GL_2X && !defined __APPLE__
219    /* FIXME: this has nothing to do here! */
220    glDisable(GL_ALPHA_TEST);
221#   endif
222#endif
223}
224
225void VertexDeclaration::Unbind()
226{
227#if defined _XBOX || defined USE_D3D9
228    int stream = -1;
229    for (int i = 0; i < m_count; i++)
230        if (m_streams[i].index != stream)
231        {
232            stream = m_streams[i].index;
233            if (FAILED(g_d3ddevice->SetStreamSource(stream, 0, 0, 0)))
234                Abort();
235        }
236    if (FAILED(g_d3ddevice->SetVertexDeclaration(NULL)))
237        Abort();
238#elif !defined __CELLOS_LV2__
239    for (int i = 0; i < m_count; i++)
240    {
241        if (m_streams[i].reg >= 0)
242        {
243            for (int j = i + 1; j < m_count; j++)
244                if (m_streams[j].reg == m_streams[i].reg)
245                    m_streams[j].reg = -1;
246
247            glDisableVertexAttribArray(m_streams[i].reg);
248        }
249    }
250    glBindBuffer(GL_ARRAY_BUFFER, 0);
251    /* FIXME: only useful for VAOs? */
252    //glBindBuffer(GL_ARRAY_BUFFER, 0);
253#else
254    /* Or even: */
255    glDisableClientState(GL_VERTEX_ARRAY);
256    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
257    glDisableClientState(GL_NORMAL_ARRAY);
258    glDisableClientState(GL_COLOR_ARRAY);
259#endif
260}
261
262void VertexDeclaration::SetStream(VertexBuffer *vb, ShaderAttrib attr1,
263                                                    ShaderAttrib attr2,
264                                                    ShaderAttrib attr3,
265                                                    ShaderAttrib attr4,
266                                                    ShaderAttrib attr5,
267                                                    ShaderAttrib attr6,
268                                                    ShaderAttrib attr7,
269                                                    ShaderAttrib attr8,
270                                                    ShaderAttrib attr9,
271                                                    ShaderAttrib attr10,
272                                                    ShaderAttrib attr11,
273                                                    ShaderAttrib attr12)
274{
275    if (!vb->m_data->m_size)
276        return;
277
278#if defined _XBOX || defined USE_D3D9
279    /* Only the first item is required to know which stream this
280     * is about; the rest of the information is stored in the
281     * vertex declaration already. */
282    uint32_t usage = (attr1.m_flags >> 16) & 0xffff;
283    uint32_t index = attr1.m_flags & 0xffff;
284
285    /* Find the stream number */
286    int usage_index = 0, stream = -1;
287    for (int i = 0; i < m_count; i++)
288        if (m_streams[i].usage == usage)
289            if (usage_index++ == index)
290            {
291                stream = m_streams[i].index;
292                break;
293            }
294
295    /* Compute this stream's stride */
296    int stride = 0;
297    for (int i = 0; i < m_count; i++)
298        if (stream == m_streams[i].index)
299            stride += m_streams[i].size;
300
301    /* Now we know the stream index and the element stride */
302    /* FIXME: precompute most of the crap above! */
303    if (stream >= 0)
304    {
305        if (FAILED(g_d3ddevice->SetStreamSource(stream, vb->m_data->m_vbo, 0, stride)))
306            Abort();
307    }
308#else
309    glBindBuffer(GL_ARRAY_BUFFER, vb->m_data->m_vbo);
310    ShaderAttrib l[12] = { attr1, attr2, attr3, attr4, attr5, attr6,
311                           attr7, attr8, attr9, attr10, attr11, attr12 };
312    for (int n = 0; n < 12 && l[n].m_flags != (uint64_t)0 - 1; n++)
313    {
314        uint32_t reg = l[n].m_flags >> 32;
315        uint32_t usage = (l[n].m_flags >> 16) & 0xffff;
316        uint32_t index = l[n].m_flags & 0xffff;
317
318#   if !defined __CELLOS_LV2__
319        glEnableVertexAttribArray((GLint)reg);
320#   else
321        switch (usage)
322        {
323        case VertexUsage::Position:
324            glEnableClientState(GL_VERTEX_ARRAY);
325            break;
326        case VertexUsage::TexCoord:
327            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
328            break;
329        case VertexUsage::Normal:
330            glEnableClientState(GL_NORMAL_ARRAY);
331            break;
332        case VertexUsage::Color:
333            glEnableClientState(GL_COLOR_ARRAY);
334            break;
335        }
336#   endif
337
338        /* We need to parse the whole vertex declaration to retrieve
339         * the information. It sucks. */
340
341        int attr_index = 0, usage_index = 0;
342        /* First, find the stream index */
343        for (; attr_index < m_count; attr_index++)
344            if (m_streams[attr_index].usage == usage)
345                if (usage_index++ == index)
346                    break;
347
348        /* Now compute the stride and offset up to this stream index */
349        int stride = 0, offset = 0;
350        for (int i = 0; i < m_count; i++)
351            if (m_streams[i].index == m_streams[attr_index].index)
352            {
353                /* Remember the register used for this stream */
354                m_streams[i].reg = reg;
355
356                stride += m_streams[i].size;
357                if (i < attr_index)
358                    offset += m_streams[i].size;
359            }
360
361        /* Finally, we need to retrieve the type of the data */
362#   if !defined GL_DOUBLE
363#       define GL_DOUBLE 0
364#   endif
365        static struct { GLint size; GLenum type; } const tlut[] =
366        {
367            { 0, 0 },
368            { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* half */
369            { 1, GL_FLOAT }, { 2, GL_FLOAT }, { 3, GL_FLOAT },
370                { 4, GL_FLOAT }, /* float */
371            { 1, GL_DOUBLE }, { 2, GL_DOUBLE }, { 3, GL_DOUBLE },
372                { 4, GL_DOUBLE }, /* double */
373            { 1, GL_BYTE }, { 2, GL_BYTE }, { 3, GL_BYTE },
374                { 4, GL_BYTE }, /* int8_t */
375            { 1, GL_UNSIGNED_BYTE }, { 2, GL_UNSIGNED_BYTE },
376                { 3, GL_UNSIGNED_BYTE }, { 4, GL_UNSIGNED_BYTE }, /* uint8_t */
377            { 1, GL_SHORT }, { 2, GL_SHORT }, { 3, GL_SHORT },
378                { 4, GL_SHORT }, /* int16_t */
379            { 1, GL_UNSIGNED_SHORT }, { 2, GL_UNSIGNED_SHORT }, { 3,
380                GL_UNSIGNED_SHORT }, { 4, GL_UNSIGNED_SHORT }, /* uint16_t */
381            { 1, GL_INT }, { 2, GL_INT }, { 3, GL_INT },
382                { 4, GL_INT }, /* int32_t */
383            { 1, GL_UNSIGNED_INT }, { 2, GL_UNSIGNED_INT },
384                { 3, GL_UNSIGNED_INT }, { 4, GL_UNSIGNED_INT }, /* uint32_t */
385        };
386
387        int type_index = m_streams[attr_index].stream_type;
388        if (type_index < 0 || type_index >= sizeof(tlut) / sizeof(*tlut))
389            type_index = 0;
390
391
392#   if !defined __CELLOS_LV2__
393        if (tlut[type_index].type == GL_FLOAT
394             || tlut[type_index].type == GL_DOUBLE
395             || tlut[type_index].type == GL_BYTE
396             || tlut[type_index].type == GL_UNSIGNED_BYTE
397#       if defined USE_GLEW
398             /* If this is not available, don't use it */
399             || !glVertexAttribIPointer
400#       endif
401             || false)
402        {
403            /* Normalize unsigned bytes by default, because it's usually
404             * some color information. */
405            GLboolean normalize = (tlut[type_index].type == GL_UNSIGNED_BYTE)
406                               || (tlut[type_index].type == GL_BYTE);
407            glVertexAttribPointer((GLint)reg, tlut[type_index].size,
408                                  tlut[type_index].type, normalize,
409                                  stride, (GLvoid const *)(uintptr_t)offset);
410        }
411#       if defined GL_VERSION_3_0
412        else
413        {
414            glVertexAttribIPointer((GLint)reg, tlut[type_index].size,
415                                   tlut[type_index].type,
416                                   stride, (GLvoid const *)(uintptr_t)offset);
417        }
418#       endif
419#   else
420        switch (usage)
421        {
422        case VertexUsage::Position:
423            glVertexPointer(tlut[type_index].size, tlut[type_index].type,
424                            stride, (GLvoid const *)(uintptr_t)offset);
425            break;
426        case VertexUsage::Normal:
427            glNormalPointer(tlut[type_index].type,
428                            stride, (GLvoid const *)(uintptr_t)offset);
429            break;
430        case VertexUsage::Color:
431            glColorPointer(tlut[type_index].size, tlut[type_index].type,
432                           stride, (GLvoid const *)(uintptr_t)offset);
433            break;
434        default:
435            Log::Error("vertex usage %d is not supported yet\n");
436            break;
437        }
438#   endif
439    }
440#endif
441}
442
443void VertexDeclaration::Initialize()
444{
445#if defined _XBOX || defined USE_D3D9
446    static D3DVERTEXELEMENT9 const end_element[] = { D3DDECL_END() };
447    static D3DDECLTYPE const X = D3DDECLTYPE_UNUSED;
448    static D3DDECLTYPE const tlut[] =
449    {
450        D3DDECLTYPE_UNUSED,
451        X, D3DDECLTYPE_FLOAT16_2, X, D3DDECLTYPE_FLOAT16_4, /* half */
452        D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3,
453            D3DDECLTYPE_FLOAT4, /* float */
454        X, X, X, X, /* double */
455        X, X, X, X, /* int8_t */
456        X, X, X, D3DDECLTYPE_UBYTE4N, /* uint8_t */
457        X, D3DDECLTYPE_SHORT2N, X, D3DDECLTYPE_SHORT4N, /* int16_t */
458        X, D3DDECLTYPE_USHORT2N, X, D3DDECLTYPE_USHORT4N, /* uint16_t */
459        X, X, X, X, /* int32_t */
460        X, X, X, X, /* uint32_t */
461    };
462    static D3DDECLUSAGE const ulut[] =
463    {
464        D3DDECLUSAGE_POSITION,
465        D3DDECLUSAGE_BLENDWEIGHT,
466        D3DDECLUSAGE_BLENDINDICES,
467        D3DDECLUSAGE_NORMAL,
468        D3DDECLUSAGE_PSIZE,
469        D3DDECLUSAGE_TEXCOORD,
470        D3DDECLUSAGE_TANGENT,
471        D3DDECLUSAGE_BINORMAL,
472        D3DDECLUSAGE_TESSFACTOR,
473#if defined _XBOX
474        D3DDECLUSAGE_TEXCOORD, /* FIXME: nonexistent */
475#else
476        D3DDECLUSAGE_POSITIONT,
477#endif
478        D3DDECLUSAGE_COLOR,
479        D3DDECLUSAGE_FOG,
480        D3DDECLUSAGE_DEPTH,
481        D3DDECLUSAGE_SAMPLE,
482    };
483
484    D3DVERTEXELEMENT9 elements[12 + 1];
485    for (int n = 0; n < m_count; n++)
486    {
487        elements[n].Stream = m_streams[n].index;
488        elements[n].Offset = 0;
489        for (int i = 0; i < n; i++)
490            if (m_streams[i].index == m_streams[n].index)
491                elements[n].Offset += m_streams[i].size;
492
493        if (m_streams[n].stream_type >= 0
494             && m_streams[n].stream_type < sizeof(tlut) / sizeof(*tlut))
495            elements[n].Type = tlut[m_streams[n].stream_type];
496        else
497            elements[n].Type = D3DDECLTYPE_UNUSED;
498
499        elements[n].Method = D3DDECLMETHOD_DEFAULT;
500
501        if (m_streams[n].usage >= 0
502             && m_streams[n].usage < sizeof(ulut) / sizeof(*ulut))
503            elements[n].Usage = ulut[m_streams[n].usage];
504        else
505            elements[n].Usage = D3DDECLUSAGE_POSITION;
506
507        elements[n].UsageIndex = 0;
508        for (int i = 0; i < n; i++)
509            if (elements[i].Stream == elements[n].Stream
510                 && elements[i].Usage == elements[n].Usage)
511                elements[n].UsageIndex++;
512    }
513    elements[m_count] = end_element[0];
514
515#   if defined USE_D3D9
516    IDirect3DVertexDeclaration9 *vdecl;
517#   elif defined _XBOX
518    D3DVertexDeclaration *vdecl;
519#   endif
520
521    if (FAILED(g_d3ddevice->CreateVertexDeclaration(elements, &vdecl)))
522        Abort();
523
524    m_data = vdecl;
525#else
526
527#endif
528}
529
530void VertexDeclaration::AddStream(VertexStreamBase const &s)
531{
532    int index = m_count ? m_streams[m_count - 1].index + 1 : 0;
533
534    for (int i = 0; s.m_streams[i].size; i++)
535    {
536        m_streams[m_count].stream_type = s.m_streams[i].stream_type;
537        m_streams[m_count].usage = s.m_streams[i].usage;
538        m_streams[m_count].size = s.m_streams[i].size;
539        m_streams[m_count].index = index;
540        m_streams[m_count].reg = -1;
541        m_count++;
542    }
543}
544
545//
546// The VertexBuffer class
547// ----------------------
548//
549
550VertexBuffer::VertexBuffer(size_t size)
551  : m_data(new VertexBufferData)
552{
553    m_data->m_size = size;
554    if (!size)
555        return;
556#if defined USE_D3D9 || defined _XBOX
557    if (FAILED(g_d3ddevice->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, NULL,
558                                               D3DPOOL_MANAGED, &m_data->m_vbo, NULL)))
559        Abort();
560#else
561    glGenBuffers(1, &m_data->m_vbo);
562    m_data->m_memory = new uint8_t[size];
563#endif
564}
565
566VertexBuffer::~VertexBuffer()
567{
568    if (m_data->m_size)
569    {
570#if defined USE_D3D9 || defined _XBOX
571        if (FAILED(m_data->m_vbo->Release()))
572            Abort();
573#else
574        glDeleteBuffers(1, &m_data->m_vbo);
575        delete[] m_data->m_memory;
576#endif
577    }
578    delete m_data;
579}
580
581void *VertexBuffer::Lock(size_t offset, size_t size)
582{
583    if (!m_data->m_size)
584        return NULL;
585#if defined USE_D3D9 || defined _XBOX
586    void *ret;
587    if (FAILED(m_data->m_vbo->Lock(offset, size, (void **)&ret, 0)))
588        Abort();
589    return ret;
590#else
591    return m_data->m_memory + offset;
592#endif
593}
594
595void VertexBuffer::Unlock()
596{
597    if (!m_data->m_size)
598        return;
599#if defined USE_D3D9 || defined _XBOX
600    if (FAILED(m_data->m_vbo->Unlock()))
601        Abort();
602#else
603    glBindBuffer(GL_ARRAY_BUFFER, m_data->m_vbo);
604    glBufferData(GL_ARRAY_BUFFER, m_data->m_size, m_data->m_memory,
605                 GL_STATIC_DRAW);
606    glBindBuffer(GL_ARRAY_BUFFER, 0);
607#endif
608}
609
610} /* namespace lol */
611
Note: See TracBrowser for help on using the repository browser.