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

Last change on this file since 1480 was 1480, checked in by sam, 9 years ago

gpu: better state restoration after vertex buffer usage.

File size: 18.3 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#   if defined HAVE_GL_2X && !defined __APPLE__
198    /* FIXME: this has nothing to do here! */
199    glEnable(GL_ALPHA_TEST);
200    glAlphaFunc(GL_GEQUAL, 0.01f);
201#   endif
202    glEnable(GL_BLEND);
203    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
204
205    switch (type)
206    {
207    case MeshPrimitive::Triangles:
208        /* FIXME: ignores most of the arguments! */
209        glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, 0);
210        break;
211    case MeshPrimitive::Points:
212        /* FIXME: ignores most of the arguments! */
213        glDrawElements(GL_POINTS, count, GL_UNSIGNED_SHORT, 0);
214        break;
215    }
216#   if defined HAVE_GL_2X && !defined __APPLE__
217    /* FIXME: this has nothing to do here! */
218    glDisable(GL_ALPHA_TEST);
219#   endif
220#endif
221}
222
223void VertexDeclaration::Unbind()
224{
225#if defined _XBOX || defined USE_D3D9
226    int stream = -1;
227    for (int i = 0; i < m_count; i++)
228        if (m_streams[i].index != stream)
229        {
230            stream = m_streams[i].index;
231            if (FAILED(g_d3ddevice->SetStreamSource(stream, 0, 0, 0)))
232                Abort();
233        }
234    if (FAILED(g_d3ddevice->SetVertexDeclaration(NULL)))
235        Abort();
236#else
237    /* FIXME: we need to unbind what we bound */
238    //glDisableVertexAttribArray(m_attrib);
239    /* FIXME: temporary kludge */
240    for (int i = 0; i < 12; i++)
241        glDisableVertexAttribArray(i);
242    glBindBuffer(GL_ARRAY_BUFFER, 0);
243    /* FIXME: only useful for VAOs */
244    //glBindBuffer(GL_ARRAY_BUFFER, 0);
245    /* Or: */
246    //glDisableVertexAttribArray(m_attrib);
247    /* Or even: */
248    //glDisableClientState(GL_VERTEX_ARRAY);
249#endif
250}
251
252void VertexDeclaration::SetStream(VertexBuffer *vb, ShaderAttrib attr1,
253                                                    ShaderAttrib attr2,
254                                                    ShaderAttrib attr3,
255                                                    ShaderAttrib attr4,
256                                                    ShaderAttrib attr5,
257                                                    ShaderAttrib attr6,
258                                                    ShaderAttrib attr7,
259                                                    ShaderAttrib attr8,
260                                                    ShaderAttrib attr9,
261                                                    ShaderAttrib attr10,
262                                                    ShaderAttrib attr11,
263                                                    ShaderAttrib attr12)
264{
265    if (!vb->m_data->m_size)
266        return;
267
268#if defined _XBOX || defined USE_D3D9
269    /* Only the first item is required to know which stream this
270     * is about; the rest of the information is stored in the
271     * vertex declaration already. */
272    uint32_t usage = (attr1.m_flags >> 16) & 0xffff;
273    uint32_t index = attr1.m_flags & 0xffff;
274
275    /* Find the stream number */
276    int usage_index = 0, stream = -1;
277    for (int i = 0; i < m_count; i++)
278        if (m_streams[i].usage == usage)
279            if (usage_index++ == index)
280            {
281                stream = m_streams[i].index;
282                break;
283            }
284
285    /* Compute this stream's stride */
286    int stride = 0;
287    for (int i = 0; i < m_count; i++)
288        if (stream == m_streams[i].index)
289            stride += m_streams[i].size;
290
291    /* Now we know the stream index and the element stride */
292    /* FIXME: precompute most of the crap above! */
293    if (stream >= 0)
294    {
295        if (FAILED(g_d3ddevice->SetStreamSource(stream, vb->m_data->m_vbo, 0, stride)))
296            Abort();
297    }
298#else
299    glBindBuffer(GL_ARRAY_BUFFER, vb->m_data->m_vbo);
300    ShaderAttrib l[12] = { attr1, attr2, attr3, attr4, attr5, attr6,
301                           attr7, attr8, attr9, attr10, attr11, attr12 };
302    for (int n = 0; n < 12 && l[n].m_flags != (uint64_t)0 - 1; n++)
303    {
304        uint32_t reg = l[n].m_flags >> 32;
305        uint32_t usage = (l[n].m_flags >> 16) & 0xffff;
306        uint32_t index = l[n].m_flags & 0xffff;
307
308#   if !defined __CELLOS_LV2__
309        glEnableVertexAttribArray((GLint)reg);
310#   else
311        switch (usage)
312        {
313        case VertexUsage::Position:
314            glEnableClientState(GL_VERTEX_ARRAY);
315            break;
316        case VertexUsage::Color:
317            glEnableClientState(GL_COLOR_ARRAY);
318            break;
319        }
320#   endif
321
322        /* We need to parse the whole vertex declaration to retrieve
323         * the information. It sucks. */
324
325        int attr_index = 0, usage_index = 0;
326        /* First, find the stream index */
327        for (; attr_index < m_count; attr_index++)
328            if (m_streams[attr_index].usage == usage)
329                if (usage_index++ == index)
330                    break;
331
332        /* Now compute the stride and offset up to this stream index */
333        int stride = 0, offset = 0;
334        for (int i = 0; i < m_count; i++)
335            if (m_streams[i].index == m_streams[attr_index].index)
336            {
337                stride += m_streams[i].size;
338                if (i < attr_index)
339                    offset += m_streams[i].size;
340            }
341
342        /* Finally, we need to retrieve the type of the data */
343#   if !defined GL_DOUBLE
344#       define GL_DOUBLE 0
345#   endif
346        static struct { GLint size; GLenum type; } const tlut[] =
347        {
348            { 0, 0 },
349            { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* half */
350            { 1, GL_FLOAT }, { 2, GL_FLOAT }, { 3, GL_FLOAT },
351                { 4, GL_FLOAT }, /* float */
352            { 1, GL_DOUBLE }, { 2, GL_DOUBLE }, { 3, GL_DOUBLE },
353                { 4, GL_DOUBLE }, /* double */
354            { 1, GL_BYTE }, { 2, GL_BYTE }, { 3, GL_BYTE },
355                { 4, GL_BYTE }, /* int8_t */
356            { 1, GL_UNSIGNED_BYTE }, { 2, GL_UNSIGNED_BYTE },
357                { 3, GL_UNSIGNED_BYTE }, { 4, GL_UNSIGNED_BYTE }, /* uint8_t */
358            { 1, GL_SHORT }, { 2, GL_SHORT }, { 3, GL_SHORT },
359                { 4, GL_SHORT }, /* int16_t */
360            { 1, GL_UNSIGNED_SHORT }, { 2, GL_UNSIGNED_SHORT }, { 3,
361                GL_UNSIGNED_SHORT }, { 4, GL_UNSIGNED_SHORT }, /* uint16_t */
362            { 1, GL_INT }, { 2, GL_INT }, { 3, GL_INT },
363                { 4, GL_INT }, /* int32_t */
364            { 1, GL_UNSIGNED_INT }, { 2, GL_UNSIGNED_INT },
365                { 3, GL_UNSIGNED_INT }, { 4, GL_UNSIGNED_INT }, /* uint32_t */
366        };
367
368        int type_index = m_streams[attr_index].stream_type;
369        if (type_index < 0 || type_index >= sizeof(tlut) / sizeof(*tlut))
370            type_index = 0;
371
372
373#   if !defined __CELLOS_LV2__
374        if (tlut[type_index].type == GL_FLOAT
375             || tlut[type_index].type == GL_DOUBLE
376             || tlut[type_index].type == GL_BYTE
377             || tlut[type_index].type == GL_UNSIGNED_BYTE)
378        {
379            /* Normalize unsigned bytes by default, because it's usually
380             * some color information. */
381            GLboolean normalize = (tlut[type_index].type == GL_UNSIGNED_BYTE)
382                               || (tlut[type_index].type == GL_BYTE);
383            glVertexAttribPointer((GLint)reg, tlut[type_index].size,
384                                  tlut[type_index].type, normalize,
385                                  stride, (GLvoid const *)(uintptr_t)offset);
386        }
387        else
388        {
389            glVertexAttribIPointer((GLint)reg, tlut[type_index].size,
390                                   tlut[type_index].type,
391                                   stride, (GLvoid const *)(uintptr_t)offset);
392        }
393#   else
394        switch (usage)
395        {
396        case VertexUsage::Position:
397            glVertexPointer(tlut[type_index].size, tlut[type_index].type,
398                            stride, (GLvoid const *)(uintptr_t)offset);
399            break;
400        case VertexUsage::Normal:
401            glNormalPointer(tlut[type_index].type,
402                            stride, (GLvoid const *)(uintptr_t)offset);
403            break;
404        case VertexUsage::Color:
405            glColorPointer(tlut[type_index].size, tlut[type_index].type,
406                           stride, (GLvoid const *)(uintptr_t)offset);
407            break;
408        }
409#   endif
410    }
411#endif
412}
413
414void VertexDeclaration::Initialize()
415{
416#if defined _XBOX || defined USE_D3D9
417    static D3DVERTEXELEMENT9 const end_element[] = { D3DDECL_END() };
418    static D3DDECLTYPE const X = D3DDECLTYPE_UNUSED;
419    static D3DDECLTYPE const tlut[] =
420    {
421        D3DDECLTYPE_UNUSED,
422        X, D3DDECLTYPE_FLOAT16_2, X, D3DDECLTYPE_FLOAT16_4, /* half */
423        D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3,
424            D3DDECLTYPE_FLOAT4, /* float */
425        X, X, X, X, /* double */
426        X, X, X, X, /* int8_t */
427        X, X, X, D3DDECLTYPE_UBYTE4N, /* uint8_t */
428        X, D3DDECLTYPE_SHORT2N, X, D3DDECLTYPE_SHORT4N, /* int16_t */
429        X, D3DDECLTYPE_USHORT2N, X, D3DDECLTYPE_USHORT4N, /* uint16_t */
430        X, X, X, X, /* int32_t */
431        X, X, X, X, /* uint32_t */
432    };
433    static D3DDECLUSAGE const ulut[] =
434    {
435        D3DDECLUSAGE_POSITION,
436        D3DDECLUSAGE_BLENDWEIGHT,
437        D3DDECLUSAGE_BLENDINDICES,
438        D3DDECLUSAGE_NORMAL,
439        D3DDECLUSAGE_PSIZE,
440        D3DDECLUSAGE_TEXCOORD,
441        D3DDECLUSAGE_TANGENT,
442        D3DDECLUSAGE_BINORMAL,
443        D3DDECLUSAGE_TESSFACTOR,
444#if defined _XBOX
445        D3DDECLUSAGE_TEXCOORD, /* FIXME: nonexistent */
446#else
447        D3DDECLUSAGE_POSITIONT,
448#endif
449        D3DDECLUSAGE_COLOR,
450        D3DDECLUSAGE_FOG,
451        D3DDECLUSAGE_DEPTH,
452        D3DDECLUSAGE_SAMPLE,
453    };
454
455    D3DVERTEXELEMENT9 elements[12 + 1];
456    for (int n = 0; n < m_count; n++)
457    {
458        elements[n].Stream = m_streams[n].index;
459        elements[n].Offset = 0;
460        for (int i = 0; i < n; i++)
461            if (m_streams[i].index == m_streams[n].index)
462                elements[n].Offset += m_streams[i].size;
463
464        if (m_streams[n].stream_type >= 0
465             && m_streams[n].stream_type < sizeof(tlut) / sizeof(*tlut))
466            elements[n].Type = tlut[m_streams[n].stream_type];
467        else
468            elements[n].Type = D3DDECLTYPE_UNUSED;
469
470        elements[n].Method = D3DDECLMETHOD_DEFAULT;
471
472        if (m_streams[n].usage >= 0
473             && m_streams[n].usage < sizeof(ulut) / sizeof(*ulut))
474            elements[n].Usage = ulut[m_streams[n].usage];
475        else
476            elements[n].Usage = D3DDECLUSAGE_POSITION;
477
478        elements[n].UsageIndex = 0;
479        for (int i = 0; i < n; i++)
480            if (elements[i].Stream == elements[n].Stream
481                 && elements[i].Usage == elements[n].Usage)
482                elements[n].UsageIndex++;
483    }
484    elements[m_count] = end_element[0];
485
486#   if defined USE_D3D9
487    IDirect3DVertexDeclaration9 *vdecl;
488#   elif defined _XBOX
489    D3DVertexDeclaration *vdecl;
490#   endif
491
492    if (FAILED(g_d3ddevice->CreateVertexDeclaration(elements, &vdecl)))
493        Abort();
494
495    m_data = vdecl;
496#else
497
498#endif
499}
500
501void VertexDeclaration::AddStream(VertexStreamBase const &s)
502{
503    int index = m_count ? m_streams[m_count - 1].index + 1 : 0;
504
505    for (int i = 0; s.m_streams[i].size; i++)
506    {
507        m_streams[m_count].stream_type = s.m_streams[i].stream_type;
508        m_streams[m_count].usage = s.m_streams[i].usage;
509        m_streams[m_count].size = s.m_streams[i].size;
510        m_streams[m_count].index = index;
511        m_count++;
512    }
513}
514
515//
516// The VertexBuffer class
517// ----------------------
518//
519
520VertexBuffer::VertexBuffer(size_t size)
521  : m_data(new VertexBufferData)
522{
523    m_data->m_size = size;
524    if (!size)
525        return;
526#if defined USE_D3D9 || defined _XBOX
527    if (FAILED(g_d3ddevice->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, NULL,
528                                               D3DPOOL_MANAGED, &m_data->m_vbo, NULL)))
529        Abort();
530#elif !defined __CELLOS_LV2__
531    glGenBuffers(1, &m_data->m_vbo);
532    m_data->m_memory = new uint8_t[size];
533#endif
534}
535
536VertexBuffer::~VertexBuffer()
537{
538    if (m_data->m_size)
539    {
540#if defined USE_D3D9 || defined _XBOX
541        if (FAILED(m_data->m_vbo->Release()))
542            Abort();
543#elif !defined __CELLOS_LV2__
544        glDeleteBuffers(1, &m_data->m_vbo);
545        delete[] m_data->m_memory;
546#endif
547    }
548    delete m_data;
549}
550
551void *VertexBuffer::Lock(size_t offset, size_t size)
552{
553    if (!m_data->m_size)
554        return NULL;
555#if defined USE_D3D9 || defined _XBOX
556    void *ret;
557    if (FAILED(m_data->m_vbo->Lock(offset, size, (void **)&ret, 0)))
558        Abort();
559    return ret;
560#elif !defined __CELLOS_LV2__
561    return m_data->m_memory + offset;
562#endif
563}
564
565void VertexBuffer::Unlock()
566{
567    if (!m_data->m_size)
568        return;
569#if defined USE_D3D9 || defined _XBOX
570    if (FAILED(m_data->m_vbo->Unlock()))
571        Abort();
572#elif !defined __CELLOS_LV2__
573    glBindBuffer(GL_ARRAY_BUFFER, m_data->m_vbo);
574    glBufferData(GL_ARRAY_BUFFER, m_data->m_size, m_data->m_memory,
575                 GL_STATIC_DRAW);
576    glBindBuffer(GL_ARRAY_BUFFER, 0);
577#endif
578}
579
580} /* namespace lol */
581
Note: See TracBrowser for help on using the repository browser.