1 //
2 // Copyright 2014 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 // BufferD3D.cpp Defines common functionality between the Buffer9 and Buffer11 classes.
8 
9 #include "libANGLE/renderer/d3d/BufferD3D.h"
10 
11 #include "common/mathutil.h"
12 #include "common/utilities.h"
13 #include "libANGLE/renderer/d3d/IndexBuffer.h"
14 #include "libANGLE/renderer/d3d/RendererD3D.h"
15 #include "libANGLE/renderer/d3d/VertexBuffer.h"
16 
17 namespace rx
18 {
19 
20 unsigned int BufferD3D::mNextSerial = 1;
21 
BufferD3D(const gl::BufferState & state,BufferFactoryD3D * factory)22 BufferD3D::BufferD3D(const gl::BufferState &state, BufferFactoryD3D *factory)
23     : BufferImpl(state),
24       mFactory(factory),
25       mStaticIndexBuffer(nullptr),
26       mStaticBufferCacheTotalSize(0),
27       mStaticVertexBufferOutOfDate(false),
28       mUnmodifiedDataUse(0),
29       mUsage(D3DBufferUsage::STATIC)
30 {
31     updateSerial();
32 }
33 
~BufferD3D()34 BufferD3D::~BufferD3D()
35 {
36     SafeDelete(mStaticIndexBuffer);
37 }
38 
emptyStaticBufferCache()39 void BufferD3D::emptyStaticBufferCache()
40 {
41     mStaticVertexBuffers.clear();
42     mStaticBufferCacheTotalSize = 0;
43 }
44 
updateSerial()45 void BufferD3D::updateSerial()
46 {
47     mSerial = mNextSerial++;
48 }
49 
updateD3DBufferUsage(const gl::Context * context,gl::BufferUsage usage)50 void BufferD3D::updateD3DBufferUsage(const gl::Context *context, gl::BufferUsage usage)
51 {
52     switch (usage)
53     {
54         case gl::BufferUsage::StaticCopy:
55         case gl::BufferUsage::StaticDraw:
56         case gl::BufferUsage::StaticRead:
57         case gl::BufferUsage::DynamicCopy:
58         case gl::BufferUsage::DynamicRead:
59         case gl::BufferUsage::StreamCopy:
60         case gl::BufferUsage::StreamRead:
61             mUsage = D3DBufferUsage::STATIC;
62             initializeStaticData(context);
63             break;
64 
65         case gl::BufferUsage::DynamicDraw:
66         case gl::BufferUsage::StreamDraw:
67             mUsage = D3DBufferUsage::DYNAMIC;
68             break;
69         default:
70             UNREACHABLE();
71     }
72 }
73 
initializeStaticData(const gl::Context * context)74 void BufferD3D::initializeStaticData(const gl::Context *context)
75 {
76     if (mStaticVertexBuffers.empty())
77     {
78         StaticVertexBufferInterface *newStaticBuffer = new StaticVertexBufferInterface(mFactory);
79         mStaticVertexBuffers.push_back(
80             std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer));
81     }
82     if (!mStaticIndexBuffer)
83     {
84         mStaticIndexBuffer = new StaticIndexBufferInterface(mFactory);
85     }
86 }
87 
getStaticIndexBuffer()88 StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer()
89 {
90     return mStaticIndexBuffer;
91 }
92 
getStaticVertexBuffer(const gl::VertexAttribute & attribute,const gl::VertexBinding & binding)93 StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(const gl::VertexAttribute &attribute,
94                                                               const gl::VertexBinding &binding)
95 {
96     if (mStaticVertexBuffers.empty())
97     {
98         // Early out if there aren't any static buffers at all
99         return nullptr;
100     }
101 
102     // Early out, the attribute can be added to mStaticVertexBuffer.
103     if (mStaticVertexBuffers.size() == 1 && mStaticVertexBuffers[0]->empty())
104     {
105         return mStaticVertexBuffers[0].get();
106     }
107 
108     // Cache size limiting: track the total allocated buffer sizes.
109     size_t currentTotalSize = 0;
110 
111     // At this point, see if any of the existing static buffers contains the attribute data
112     // If there is a cached static buffer that already contains the attribute, then return it
113     for (const auto &staticBuffer : mStaticVertexBuffers)
114     {
115         if (staticBuffer->matchesAttribute(attribute, binding))
116         {
117             return staticBuffer.get();
118         }
119 
120         currentTotalSize += staticBuffer->getBufferSize();
121     }
122 
123     // Cache size limiting: Clean-up threshold is four times the base buffer size, with a minimum.
124     ASSERT(getSize() < std::numeric_limits<size_t>::max() / 4u);
125     size_t sizeThreshold = std::max(getSize() * 4u, static_cast<size_t>(0x1000u));
126 
127     // If we're past the threshold, clear the buffer cache. Note that this will release buffers
128     // that are currenly bound, and in an edge case can even translate the same attribute twice
129     // in the same draw call. It will not delete currently bound buffers, however, because they
130     // are ref counted.
131     if (currentTotalSize > sizeThreshold)
132     {
133         emptyStaticBufferCache();
134     }
135 
136     // At this point, we must create a new static buffer for the attribute data.
137     StaticVertexBufferInterface *newStaticBuffer = new StaticVertexBufferInterface(mFactory);
138     newStaticBuffer->setAttribute(attribute, binding);
139     mStaticVertexBuffers.push_back(std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer));
140     return newStaticBuffer;
141 }
142 
invalidateStaticData(const gl::Context * context)143 void BufferD3D::invalidateStaticData(const gl::Context *context)
144 {
145     emptyStaticBufferCache();
146 
147     if (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0)
148     {
149         SafeDelete(mStaticIndexBuffer);
150     }
151 
152     // If the buffer was created with a static usage then we recreate the static
153     // buffers so that they are populated the next time we use this buffer.
154     if (mUsage == D3DBufferUsage::STATIC)
155     {
156         initializeStaticData(context);
157     }
158 
159     mUnmodifiedDataUse = 0;
160 }
161 
162 // Creates static buffers if sufficient used data has been left unmodified
promoteStaticUsage(const gl::Context * context,size_t dataSize)163 void BufferD3D::promoteStaticUsage(const gl::Context *context, size_t dataSize)
164 {
165     if (mUsage == D3DBufferUsage::DYNAMIC)
166     {
167         // Note: This is not a safe math operation. 'dataSize' can come from the app.
168         mUnmodifiedDataUse += dataSize;
169 
170         if (mUnmodifiedDataUse > 3 * getSize())
171         {
172             updateD3DBufferUsage(context, gl::BufferUsage::StaticDraw);
173         }
174     }
175 }
176 
getIndexRange(const gl::Context * context,gl::DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,gl::IndexRange * outRange)177 angle::Result BufferD3D::getIndexRange(const gl::Context *context,
178                                        gl::DrawElementsType type,
179                                        size_t offset,
180                                        size_t count,
181                                        bool primitiveRestartEnabled,
182                                        gl::IndexRange *outRange)
183 {
184     const uint8_t *data = nullptr;
185     ANGLE_TRY(getData(context, &data));
186 
187     *outRange = gl::ComputeIndexRange(type, data + offset, count, primitiveRestartEnabled);
188     return angle::Result::Continue;
189 }
190 
191 }  // namespace rx
192