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

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

gpu: silently ignore empty vertex and index buffers instead of crashing
and letting the user guess what happened. This doesn't prevent us from
displaying an additional friendly warning later.

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