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

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

gpu: only unbind vertex attribs that we actually bound.

File size: 18.8 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        {
398            /* Normalize unsigned bytes by default, because it's usually
399             * some color information. */
400            GLboolean normalize = (tlut[type_index].type == GL_UNSIGNED_BYTE)
401                               || (tlut[type_index].type == GL_BYTE);
402            glVertexAttribPointer((GLint)reg, tlut[type_index].size,
403                                  tlut[type_index].type, normalize,
404                                  stride, (GLvoid const *)(uintptr_t)offset);
405        }
406#       if defined GL_VERSION_3_0
407        else
408        {
409            glVertexAttribIPointer((GLint)reg, tlut[type_index].size,
410                                   tlut[type_index].type,
411                                   stride, (GLvoid const *)(uintptr_t)offset);
412        }
413#       endif
414#   else
415        switch (usage)
416        {
417        case VertexUsage::Position:
418            glVertexPointer(tlut[type_index].size, tlut[type_index].type,
419                            stride, (GLvoid const *)(uintptr_t)offset);
420            break;
421        case VertexUsage::Normal:
422            glNormalPointer(tlut[type_index].type,
423                            stride, (GLvoid const *)(uintptr_t)offset);
424            break;
425        case VertexUsage::Color:
426            glColorPointer(tlut[type_index].size, tlut[type_index].type,
427                           stride, (GLvoid const *)(uintptr_t)offset);
428            break;
429        }
430#   endif
431    }
432#endif
433}
434
435void VertexDeclaration::Initialize()
436{
437#if defined _XBOX || defined USE_D3D9
438    static D3DVERTEXELEMENT9 const end_element[] = { D3DDECL_END() };
439    static D3DDECLTYPE const X = D3DDECLTYPE_UNUSED;
440    static D3DDECLTYPE const tlut[] =
441    {
442        D3DDECLTYPE_UNUSED,
443        X, D3DDECLTYPE_FLOAT16_2, X, D3DDECLTYPE_FLOAT16_4, /* half */
444        D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3,
445            D3DDECLTYPE_FLOAT4, /* float */
446        X, X, X, X, /* double */
447        X, X, X, X, /* int8_t */
448        X, X, X, D3DDECLTYPE_UBYTE4N, /* uint8_t */
449        X, D3DDECLTYPE_SHORT2N, X, D3DDECLTYPE_SHORT4N, /* int16_t */
450        X, D3DDECLTYPE_USHORT2N, X, D3DDECLTYPE_USHORT4N, /* uint16_t */
451        X, X, X, X, /* int32_t */
452        X, X, X, X, /* uint32_t */
453    };
454    static D3DDECLUSAGE const ulut[] =
455    {
456        D3DDECLUSAGE_POSITION,
457        D3DDECLUSAGE_BLENDWEIGHT,
458        D3DDECLUSAGE_BLENDINDICES,
459        D3DDECLUSAGE_NORMAL,
460        D3DDECLUSAGE_PSIZE,
461        D3DDECLUSAGE_TEXCOORD,
462        D3DDECLUSAGE_TANGENT,
463        D3DDECLUSAGE_BINORMAL,
464        D3DDECLUSAGE_TESSFACTOR,
465#if defined _XBOX
466        D3DDECLUSAGE_TEXCOORD, /* FIXME: nonexistent */
467#else
468        D3DDECLUSAGE_POSITIONT,
469#endif
470        D3DDECLUSAGE_COLOR,
471        D3DDECLUSAGE_FOG,
472        D3DDECLUSAGE_DEPTH,
473        D3DDECLUSAGE_SAMPLE,
474    };
475
476    D3DVERTEXELEMENT9 elements[12 + 1];
477    for (int n = 0; n < m_count; n++)
478    {
479        elements[n].Stream = m_streams[n].index;
480        elements[n].Offset = 0;
481        for (int i = 0; i < n; i++)
482            if (m_streams[i].index == m_streams[n].index)
483                elements[n].Offset += m_streams[i].size;
484
485        if (m_streams[n].stream_type >= 0
486             && m_streams[n].stream_type < sizeof(tlut) / sizeof(*tlut))
487            elements[n].Type = tlut[m_streams[n].stream_type];
488        else
489            elements[n].Type = D3DDECLTYPE_UNUSED;
490
491        elements[n].Method = D3DDECLMETHOD_DEFAULT;
492
493        if (m_streams[n].usage >= 0
494             && m_streams[n].usage < sizeof(ulut) / sizeof(*ulut))
495            elements[n].Usage = ulut[m_streams[n].usage];
496        else
497            elements[n].Usage = D3DDECLUSAGE_POSITION;
498
499        elements[n].UsageIndex = 0;
500        for (int i = 0; i < n; i++)
501            if (elements[i].Stream == elements[n].Stream
502                 && elements[i].Usage == elements[n].Usage)
503                elements[n].UsageIndex++;
504    }
505    elements[m_count] = end_element[0];
506
507#   if defined USE_D3D9
508    IDirect3DVertexDeclaration9 *vdecl;
509#   elif defined _XBOX
510    D3DVertexDeclaration *vdecl;
511#   endif
512
513    if (FAILED(g_d3ddevice->CreateVertexDeclaration(elements, &vdecl)))
514        Abort();
515
516    m_data = vdecl;
517#else
518
519#endif
520}
521
522void VertexDeclaration::AddStream(VertexStreamBase const &s)
523{
524    int index = m_count ? m_streams[m_count - 1].index + 1 : 0;
525
526    for (int i = 0; s.m_streams[i].size; i++)
527    {
528        m_streams[m_count].stream_type = s.m_streams[i].stream_type;
529        m_streams[m_count].usage = s.m_streams[i].usage;
530        m_streams[m_count].size = s.m_streams[i].size;
531        m_streams[m_count].index = index;
532        m_streams[m_count].reg = -1;
533        m_count++;
534    }
535}
536
537//
538// The VertexBuffer class
539// ----------------------
540//
541
542VertexBuffer::VertexBuffer(size_t size)
543  : m_data(new VertexBufferData)
544{
545    m_data->m_size = size;
546    if (!size)
547        return;
548#if defined USE_D3D9 || defined _XBOX
549    if (FAILED(g_d3ddevice->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, NULL,
550                                               D3DPOOL_MANAGED, &m_data->m_vbo, NULL)))
551        Abort();
552#else
553    glGenBuffers(1, &m_data->m_vbo);
554    m_data->m_memory = new uint8_t[size];
555#endif
556}
557
558VertexBuffer::~VertexBuffer()
559{
560    if (m_data->m_size)
561    {
562#if defined USE_D3D9 || defined _XBOX
563        if (FAILED(m_data->m_vbo->Release()))
564            Abort();
565#else
566        glDeleteBuffers(1, &m_data->m_vbo);
567        delete[] m_data->m_memory;
568#endif
569    }
570    delete m_data;
571}
572
573void *VertexBuffer::Lock(size_t offset, size_t size)
574{
575    if (!m_data->m_size)
576        return NULL;
577#if defined USE_D3D9 || defined _XBOX
578    void *ret;
579    if (FAILED(m_data->m_vbo->Lock(offset, size, (void **)&ret, 0)))
580        Abort();
581    return ret;
582#else
583    return m_data->m_memory + offset;
584#endif
585}
586
587void VertexBuffer::Unlock()
588{
589    if (!m_data->m_size)
590        return;
591#if defined USE_D3D9 || defined _XBOX
592    if (FAILED(m_data->m_vbo->Unlock()))
593        Abort();
594#else
595    glBindBuffer(GL_ARRAY_BUFFER, m_data->m_vbo);
596    glBufferData(GL_ARRAY_BUFFER, m_data->m_size, m_data->m_memory,
597                 GL_STATIC_DRAW);
598    glBindBuffer(GL_ARRAY_BUFFER, 0);
599#endif
600}
601
602} /* namespace lol */
603
Note: See TracBrowser for help on using the repository browser.