1//
2// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// VertexBuffer9.cpp: Defines the D3D9 VertexBuffer implementation.
8
9#include "libGLESv2/renderer/d3d/d3d9/VertexBuffer9.h"
10#include "libGLESv2/renderer/d3d/d3d9/Renderer9.h"
11#include "libGLESv2/renderer/d3d/d3d9/formatutils9.h"
12#include "libGLESv2/renderer/vertexconversion.h"
13#include "libGLESv2/renderer/BufferImpl.h"
14#include "libGLESv2/VertexAttribute.h"
15#include "libGLESv2/Buffer.h"
16
17namespace rx
18{
19
20VertexBuffer9::VertexBuffer9(rx::Renderer9 *renderer) : mRenderer(renderer)
21{
22    mVertexBuffer = NULL;
23    mBufferSize = 0;
24    mDynamicUsage = false;
25}
26
27VertexBuffer9::~VertexBuffer9()
28{
29    SafeRelease(mVertexBuffer);
30}
31
32gl::Error VertexBuffer9::initialize(unsigned int size, bool dynamicUsage)
33{
34    SafeRelease(mVertexBuffer);
35
36    updateSerial();
37
38    if (size > 0)
39    {
40        DWORD flags = D3DUSAGE_WRITEONLY;
41        if (dynamicUsage)
42        {
43            flags |= D3DUSAGE_DYNAMIC;
44        }
45
46        HRESULT result = mRenderer->createVertexBuffer(size, flags, &mVertexBuffer);
47
48        if (FAILED(result))
49        {
50            return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal vertex buffer of size, %lu.", size);
51        }
52    }
53
54    mBufferSize = size;
55    mDynamicUsage = dynamicUsage;
56    return gl::Error(GL_NO_ERROR);
57}
58
59VertexBuffer9 *VertexBuffer9::makeVertexBuffer9(VertexBuffer *vertexBuffer)
60{
61    ASSERT(HAS_DYNAMIC_TYPE(VertexBuffer9*, vertexBuffer));
62    return static_cast<VertexBuffer9*>(vertexBuffer);
63}
64
65gl::Error VertexBuffer9::storeVertexAttributes(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData &currentValue,
66                                               GLint start, GLsizei count, GLsizei instances, unsigned int offset)
67{
68    if (!mVertexBuffer)
69    {
70        return gl::Error(GL_OUT_OF_MEMORY, "Internal vertex buffer is not initialized.");
71    }
72
73    gl::Buffer *buffer = attrib.buffer.get();
74
75    int inputStride = gl::ComputeVertexAttributeStride(attrib);
76    int elementSize = gl::ComputeVertexAttributeTypeSize(attrib);
77
78    DWORD lockFlags = mDynamicUsage ? D3DLOCK_NOOVERWRITE : 0;
79
80    uint8_t *mapPtr = NULL;
81
82    unsigned int mapSize;
83    gl::Error error = spaceRequired(attrib, count, instances, &mapSize);
84    if (error.isError())
85    {
86        return error;
87    }
88
89    HRESULT result = mVertexBuffer->Lock(offset, mapSize, reinterpret_cast<void**>(&mapPtr), lockFlags);
90    if (FAILED(result))
91    {
92        return gl::Error(GL_OUT_OF_MEMORY, "Failed to lock internal vertex buffer, HRESULT: 0x%08x.", result);
93    }
94
95    const uint8_t *input = NULL;
96    if (attrib.enabled)
97    {
98        if (buffer)
99        {
100            BufferImpl *storage = buffer->getImplementation();
101            input = static_cast<const uint8_t*>(storage->getData()) + static_cast<int>(attrib.offset);
102        }
103        else
104        {
105            input = static_cast<const uint8_t*>(attrib.pointer);
106        }
107    }
108    else
109    {
110        input = reinterpret_cast<const uint8_t*>(currentValue.FloatValues);
111    }
112
113    if (instances == 0 || attrib.divisor == 0)
114    {
115        input += inputStride * start;
116    }
117
118    gl::VertexFormat vertexFormat(attrib, currentValue.Type);
119    const d3d9::VertexFormat &d3dVertexInfo = d3d9::GetVertexFormatInfo(mRenderer->getCapsDeclTypes(), vertexFormat);
120    bool needsConversion = (d3dVertexInfo.conversionType & VERTEX_CONVERT_CPU) > 0;
121
122    if (!needsConversion && inputStride == elementSize)
123    {
124        size_t copySize = static_cast<size_t>(count) * static_cast<size_t>(inputStride);
125        memcpy(mapPtr, input, copySize);
126    }
127    else
128    {
129        d3dVertexInfo.copyFunction(input, inputStride, count, mapPtr);
130    }
131
132    mVertexBuffer->Unlock();
133
134    return gl::Error(GL_NO_ERROR);
135}
136
137gl::Error VertexBuffer9::getSpaceRequired(const gl::VertexAttribute &attrib, GLsizei count, GLsizei instances,
138                                          unsigned int *outSpaceRequired) const
139{
140    return spaceRequired(attrib, count, instances, outSpaceRequired);
141}
142
143unsigned int VertexBuffer9::getBufferSize() const
144{
145    return mBufferSize;
146}
147
148gl::Error VertexBuffer9::setBufferSize(unsigned int size)
149{
150    if (size > mBufferSize)
151    {
152        return initialize(size, mDynamicUsage);
153    }
154    else
155    {
156        return gl::Error(GL_NO_ERROR);
157    }
158}
159
160gl::Error VertexBuffer9::discard()
161{
162    if (!mVertexBuffer)
163    {
164        return gl::Error(GL_OUT_OF_MEMORY, "Internal vertex buffer is not initialized.");
165    }
166
167    void *dummy;
168    HRESULT result;
169
170    result = mVertexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
171    if (FAILED(result))
172    {
173        return gl::Error(GL_OUT_OF_MEMORY, "Failed to lock internal buffer for discarding, HRESULT: 0x%08x", result);
174    }
175
176    result = mVertexBuffer->Unlock();
177    if (FAILED(result))
178    {
179        return gl::Error(GL_OUT_OF_MEMORY, "Failed to unlock internal buffer for discarding, HRESULT: 0x%08x", result);
180    }
181
182    return gl::Error(GL_NO_ERROR);
183}
184
185IDirect3DVertexBuffer9 * VertexBuffer9::getBuffer() const
186{
187    return mVertexBuffer;
188}
189
190gl::Error VertexBuffer9::spaceRequired(const gl::VertexAttribute &attrib, std::size_t count, GLsizei instances,
191                                       unsigned int *outSpaceRequired) const
192{
193    gl::VertexFormat vertexFormat(attrib, GL_FLOAT);
194    const d3d9::VertexFormat &d3d9VertexInfo = d3d9::GetVertexFormatInfo(mRenderer->getCapsDeclTypes(), vertexFormat);
195
196    if (attrib.enabled)
197    {
198        unsigned int elementCount = 0;
199        if (instances == 0 || attrib.divisor == 0)
200        {
201            elementCount = count;
202        }
203        else
204        {
205            // Round up to divisor, if possible
206            elementCount = rx::UnsignedCeilDivide(static_cast<unsigned int>(instances), attrib.divisor);
207        }
208
209        if (d3d9VertexInfo.outputElementSize <= std::numeric_limits<unsigned int>::max() / elementCount)
210        {
211            if (outSpaceRequired)
212            {
213                *outSpaceRequired = d3d9VertexInfo.outputElementSize * elementCount;
214            }
215            return gl::Error(GL_NO_ERROR);
216        }
217        else
218        {
219            return gl::Error(GL_OUT_OF_MEMORY, "New vertex buffer size would result in an overflow.");
220        }
221    }
222    else
223    {
224        const unsigned int elementSize = 4;
225        if (outSpaceRequired)
226        {
227            *outSpaceRequired = elementSize * 4;
228        }
229        return gl::Error(GL_NO_ERROR);
230    }
231}
232
233}
234