Commit 52b09c2f by Jamie Madill Committed by Commit Bot

Re-re-land "D3D11: Implement dirty bits for VertexArray11.""

Translated attributes are now stored in the VertexArray11 in a cache, and only updated when dirty bits change. Currently dynamic attributes must be re-translated every call, so these are stored in a list and processed repeatedly. This skips doing a lot of the VertexDataManager work for vertex attributes that don't change between draw calls. Current value attributes, which correspond to disabled attributes that the program will pulls vertex data from, are owned by the Context, so these need to be handled outside of the VertexArray11. Further changes will be necessary to reduce the redundant work we do in the InputLayoutCache. We shouldn't need to re-check the cache if nothing relevant changed. This give about a 23% performance improvement on the draw call benchmark on my machine. Re-land with a fix for the start vertex offset. Re-re-land with a fix for using XFB with deleted buffers. BUG=angleproject:1327 Change-Id: I0fba49515375c149bbf54d933f8d1f747fbb8158 Reviewed-on: https://chromium-review.googlesource.com/338003Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 8c15c574
......@@ -29,9 +29,7 @@ VertexArray::Data::~Data()
}
VertexArray::VertexArray(rx::ImplFactory *factory, GLuint id, size_t maxAttribs)
: mId(id),
mVertexArray(factory->createVertexArray(mData)),
mData(maxAttribs)
: mId(id), mData(maxAttribs), mVertexArray(factory->createVertexArray(mData))
{
}
......
......@@ -114,10 +114,10 @@ class VertexArray final : public LabeledObject
private:
GLuint mId;
rx::VertexArrayImpl *mVertexArray;
Data mData;
DirtyBits mDirtyBits;
rx::VertexArrayImpl *mVertexArray;
};
}
......
......@@ -161,13 +161,13 @@ void BufferD3D::invalidateStaticData()
// Creates static buffers if sufficient used data has been left unmodified
void BufferD3D::promoteStaticUsage(int dataSize)
{
if (mStaticVertexBuffers.empty() && !mStaticIndexBuffer)
if (mUsage == D3DBufferUsage::DYNAMIC)
{
mUnmodifiedDataUse += dataSize;
if (mUnmodifiedDataUse > 3 * getSize())
{
initializeStaticData();
updateD3DBufferUsage(GL_STATIC_DRAW);
}
}
}
......
......@@ -43,8 +43,8 @@ class BufferD3D : public BufferImpl
StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute);
StaticIndexBufferInterface *getStaticIndexBuffer();
void initializeStaticData();
void invalidateStaticData();
virtual void initializeStaticData();
virtual void invalidateStaticData();
void promoteStaticUsage(int dataSize);
......
......@@ -55,7 +55,6 @@ RendererD3D::~RendererD3D()
void RendererD3D::cleanup()
{
mTranslatedAttribCache.clear();
mScratchMemoryBuffer.resize(0);
for (auto &incompleteTexture : mIncompleteTextures)
{
......@@ -199,7 +198,7 @@ gl::Error RendererD3D::genericDrawArrays(const gl::Data &data,
if (!skipDraw(data, mode))
{
ANGLE_TRY(drawArraysImpl(data, mode, count, instances));
ANGLE_TRY(drawArraysImpl(data, mode, first, count, instances));
if (data.state->isTransformFeedbackActiveUnpaused())
{
......
......@@ -285,8 +285,6 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
void initializeDebugAnnotator();
gl::DebugAnnotator *mAnnotator;
std::vector<TranslatedAttribute> mTranslatedAttribCache;
bool mPresentPathFastEnabled;
private:
......@@ -306,6 +304,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
virtual gl::Error drawArraysImpl(const gl::Data &data,
GLenum mode,
GLint startVertex,
GLsizei count,
GLsizei instances) = 0;
virtual gl::Error drawElementsImpl(const gl::Data &data,
......
......@@ -80,7 +80,7 @@ class VertexBufferInterface : angle::NonCopyable
unsigned int getSerial() const;
VertexBuffer* getVertexBuffer() const;
VertexBuffer *getVertexBuffer() const;
protected:
gl::Error discard();
......
......@@ -10,9 +10,10 @@
#ifndef LIBANGLE_RENDERER_D3D_VERTEXDATAMANAGER_H_
#define LIBANGLE_RENDERER_D3D_VERTEXDATAMANAGER_H_
#include "common/angleutils.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/Constants.h"
#include "libANGLE/VertexAttribute.h"
#include "common/angleutils.h"
namespace gl
{
......@@ -47,11 +48,16 @@ struct TranslatedAttribute
{
TranslatedAttribute();
// Computes the correct offset from baseOffset, usesFirstVertexOffset, stride and startVertex.
// Can throw an error on integer overflow.
gl::ErrorOrResult<unsigned int> computeOffset(GLint startVertex) const;
bool active;
const gl::VertexAttribute *attribute;
GLenum currentValueType;
unsigned int offset;
unsigned int baseOffset;
bool usesFirstVertexOffset;
unsigned int stride; // 0 means not to advance the read pointer at all
VertexBufferBinding vertexBuffer;
......@@ -84,19 +90,23 @@ class VertexDataManager : angle::NonCopyable
std::vector<TranslatedAttribute> *translatedAttribs,
GLsizei instances);
static void StoreDirectAttrib(TranslatedAttribute *directAttrib, GLint start);
static void StoreDirectAttrib(TranslatedAttribute *directAttrib);
static gl::Error StoreStaticAttrib(TranslatedAttribute *translated,
GLint start,
GLsizei count,
GLsizei instances);
gl::Error storeDynamicAttribs(std::vector<TranslatedAttribute> *translatedAttribs,
const std::vector<size_t> &dynamicAttribIndexes,
const gl::AttributesMask &dynamicAttribsMask,
GLint start,
GLsizei count,
GLsizei instances);
// Promote static usage of dynamic buffers.
static void PromoteDynamicAttribs(const std::vector<TranslatedAttribute> &translatedAttribs,
const gl::AttributesMask &dynamicAttribsMask,
GLsizei count);
gl::Error storeCurrentValue(const gl::VertexAttribCurrentValueData &currentValue,
TranslatedAttribute *translated,
size_t attribIndex);
......@@ -121,13 +131,11 @@ class VertexDataManager : angle::NonCopyable
GLsizei count,
GLsizei instances);
void unmapStreamingBuffer();
BufferFactoryD3D *const mFactory;
StreamingVertexBufferInterface *mStreamingBuffer;
std::vector<CurrentValueState> mCurrentValueCache;
std::vector<size_t> mDynamicAttributeIndexesCache;
gl::AttributesMask mDynamicAttribsMaskCache;
};
} // namespace rx
......
......@@ -138,7 +138,7 @@ class Buffer11::BufferStorage : angle::NonCopyable
class Buffer11::NativeStorage : public Buffer11::BufferStorage
{
public:
NativeStorage(Renderer11 *renderer, BufferUsage usage);
NativeStorage(Renderer11 *renderer, BufferUsage usage, const NotificationSet *onStorageChanged);
~NativeStorage() override;
bool isMappable() const override { return mUsage == BUFFER_USAGE_STAGING; }
......@@ -163,6 +163,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
unsigned int bufferSize);
ID3D11Buffer *mNativeStorage;
const NotificationSet *mOnStorageChanged;
};
// A emulated indexed buffer storage represents an underlying D3D11 buffer for data
......@@ -177,7 +178,8 @@ class Buffer11::EmulatedIndexedStorage : public Buffer11::BufferStorage
bool isMappable() const override { return true; }
gl::ErrorOrResult<ID3D11Buffer *> getNativeStorage(SourceIndexData *indexInfo,
const TranslatedAttribute &attribute);
const TranslatedAttribute &attribute,
GLint startVertex);
gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source,
size_t sourceOffset,
......@@ -521,7 +523,8 @@ gl::ErrorOrResult<ID3D11Buffer *> Buffer11::getBuffer(BufferUsage usage)
gl::ErrorOrResult<ID3D11Buffer *> Buffer11::getEmulatedIndexedBuffer(
SourceIndexData *indexInfo,
const TranslatedAttribute &attribute)
const TranslatedAttribute &attribute,
GLint startVertex)
{
ASSERT(indexInfo);
......@@ -533,7 +536,8 @@ gl::ErrorOrResult<ID3D11Buffer *> Buffer11::getEmulatedIndexedBuffer(
EmulatedIndexedStorage *emulatedStorage = GetAs<EmulatedIndexedStorage>(untypedStorage);
ID3D11Buffer *nativeStorage = nullptr;
ANGLE_TRY_RESULT(emulatedStorage->getNativeStorage(indexInfo, attribute), nativeStorage);
ANGLE_TRY_RESULT(emulatedStorage->getNativeStorage(indexInfo, attribute, startVertex),
nativeStorage);
return nativeStorage;
}
......@@ -634,23 +638,7 @@ gl::ErrorOrResult<Buffer11::BufferStorage *> Buffer11::getBufferStorage(BufferUs
if (!newStorage)
{
if (usage == BUFFER_USAGE_PIXEL_PACK)
{
newStorage = new PackStorage(mRenderer);
}
else if (usage == BUFFER_USAGE_SYSTEM_MEMORY)
{
newStorage = new SystemMemoryStorage(mRenderer);
}
else if (usage == BUFFER_USAGE_EMULATED_INDEXED_VERTEX)
{
newStorage = new EmulatedIndexedStorage(mRenderer);
}
else
{
// buffer is not allocated, create it
newStorage = new NativeStorage(mRenderer, usage);
}
newStorage = allocateStorage(usage);
}
// resize buffer
......@@ -664,6 +652,23 @@ gl::ErrorOrResult<Buffer11::BufferStorage *> Buffer11::getBufferStorage(BufferUs
return newStorage;
}
Buffer11::BufferStorage *Buffer11::allocateStorage(BufferUsage usage) const
{
switch (usage)
{
case BUFFER_USAGE_PIXEL_PACK:
return new PackStorage(mRenderer);
case BUFFER_USAGE_SYSTEM_MEMORY:
return new SystemMemoryStorage(mRenderer);
case BUFFER_USAGE_EMULATED_INDEXED_VERTEX:
return new EmulatedIndexedStorage(mRenderer);
case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
return new NativeStorage(mRenderer, usage, &mDirectBufferDirtyCallbacks);
default:
return new NativeStorage(mRenderer, usage, nullptr);
}
}
gl::ErrorOrResult<Buffer11::BufferStorage *> Buffer11::getConstantBufferRangeStorage(
GLintptr offset,
GLsizeiptr size)
......@@ -677,7 +682,7 @@ gl::ErrorOrResult<Buffer11::BufferStorage *> Buffer11::getConstantBufferRangeSto
if (!cacheEntry->storage)
{
cacheEntry->storage = new NativeStorage(mRenderer, BUFFER_USAGE_UNIFORM);
cacheEntry->storage = allocateStorage(BUFFER_USAGE_UNIFORM);
cacheEntry->lruCount = ++mMaxConstantBufferLruCount;
}
......@@ -806,8 +811,43 @@ bool Buffer11::supportsDirectBinding() const
{
// Do not support direct buffers for dynamic data. The streaming buffer
// offers better performance for data which changes every frame.
// Check for absence of static buffer interfaces to detect dynamic data.
return (!mStaticVertexBuffers.empty() && mStaticIndexBuffer);
return (mUsage == D3DBufferUsage::STATIC);
}
void Buffer11::initializeStaticData()
{
BufferD3D::initializeStaticData();
// Notify when static data changes.
mStaticBufferDirtyCallbacks.signal();
}
void Buffer11::invalidateStaticData()
{
BufferD3D::invalidateStaticData();
// Notify when static data changes.
mStaticBufferDirtyCallbacks.signal();
}
void Buffer11::addStaticBufferDirtyCallback(const NotificationCallback *callback)
{
mStaticBufferDirtyCallbacks.add(callback);
}
void Buffer11::removeStaticBufferDirtyCallback(const NotificationCallback *callback)
{
mStaticBufferDirtyCallbacks.remove(callback);
}
void Buffer11::addDirectBufferDirtyCallback(const NotificationCallback *callback)
{
mDirectBufferDirtyCallbacks.add(callback);
}
void Buffer11::removeDirectBufferDirtyCallback(const NotificationCallback *callback)
{
mDirectBufferDirtyCallbacks.remove(callback);
}
Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage)
......@@ -829,8 +869,10 @@ gl::Error Buffer11::BufferStorage::setData(const uint8_t *data, size_t offset, s
return gl::NoError();
}
Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer, BufferUsage usage)
: BufferStorage(renderer, usage), mNativeStorage(nullptr)
Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer,
BufferUsage usage,
const NotificationSet *onStorageChanged)
: BufferStorage(renderer, usage), mNativeStorage(nullptr), mOnStorageChanged(onStorageChanged)
{
}
......@@ -945,6 +987,12 @@ gl::Error Buffer11::NativeStorage::resize(size_t size, bool preserveData)
mBufferSize = bufferDesc.ByteWidth;
// Notify that the storage has changed.
if (mOnStorageChanged)
{
mOnStorageChanged->signal();
}
return gl::NoError();
}
......@@ -1050,7 +1098,8 @@ Buffer11::EmulatedIndexedStorage::~EmulatedIndexedStorage()
gl::ErrorOrResult<ID3D11Buffer *> Buffer11::EmulatedIndexedStorage::getNativeStorage(
SourceIndexData *indexInfo,
const TranslatedAttribute &attribute)
const TranslatedAttribute &attribute,
GLint startVertex)
{
// If a change in the indices applied from the last draw call is detected, then the emulated
// indexed buffer needs to be invalidated. After invalidation, the change detected flag should
......@@ -1092,9 +1141,12 @@ gl::ErrorOrResult<ID3D11Buffer *> Buffer11::EmulatedIndexedStorage::getNativeSto
if (!mNativeStorage)
{
unsigned int offset = 0;
ANGLE_TRY_RESULT(attribute.computeOffset(startVertex), offset);
// Expand the memory storage upon request and cache the results.
unsigned int expandedDataSize =
static_cast<unsigned int>((indexInfo->srcCount * attribute.stride) + attribute.offset);
static_cast<unsigned int>((indexInfo->srcCount * attribute.stride) + offset);
MemoryBuffer expandedData;
if (!expandedData.resize(expandedDataSize))
{
......@@ -1111,7 +1163,7 @@ gl::ErrorOrResult<ID3D11Buffer *> Buffer11::EmulatedIndexedStorage::getNativeSto
// Ensure that we start in the correct place for the emulated data copy operation to
// maintain offset behaviors.
curr += attribute.offset;
curr += offset;
ReadIndexValueFunction readIndexValue = ReadIndexValueFromIndices<GLushort>;
......
......@@ -13,6 +13,7 @@
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/d3d/BufferD3D.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
namespace gl
{
......@@ -63,9 +64,9 @@ class Buffer11 : public BufferD3D
virtual ~Buffer11();
gl::ErrorOrResult<ID3D11Buffer *> getBuffer(BufferUsage usage);
gl::ErrorOrResult<ID3D11Buffer *> getEmulatedIndexedBuffer(
SourceIndexData *indexInfo,
const TranslatedAttribute &attribute);
gl::ErrorOrResult<ID3D11Buffer *> getEmulatedIndexedBuffer(SourceIndexData *indexInfo,
const TranslatedAttribute &attribute,
GLint startVertex);
gl::ErrorOrResult<ID3D11Buffer *> getConstantBufferRange(GLintptr offset, GLsizeiptr size);
gl::ErrorOrResult<ID3D11ShaderResourceView *> getSRV(DXGI_FORMAT srvFormat);
bool isMapped() const { return mMappedStorage != nullptr; }
......@@ -74,18 +75,32 @@ class Buffer11 : public BufferD3D
size_t getTotalCPUBufferMemoryBytes() const;
// BufferD3D implementation
virtual size_t getSize() const { return mSize; }
virtual bool supportsDirectBinding() const;
size_t getSize() const override { return mSize; }
bool supportsDirectBinding() const override;
gl::Error getData(const uint8_t **outData) override;
void initializeStaticData() override;
void invalidateStaticData() override;
// BufferImpl implementation
virtual gl::Error setData(const void* data, size_t size, GLenum usage);
virtual gl::Error setSubData(const void* data, size_t size, size_t offset);
virtual gl::Error copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size);
virtual gl::Error map(GLenum access, GLvoid **mapPtr);
virtual gl::Error mapRange(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr);
virtual gl::Error unmap(GLboolean *result);
virtual gl::Error markTransformFeedbackUsage();
gl::Error setData(const void *data, size_t size, GLenum usage) override;
gl::Error setSubData(const void *data, size_t size, size_t offset) override;
gl::Error copySubData(BufferImpl *source,
GLintptr sourceOffset,
GLintptr destOffset,
GLsizeiptr size) override;
gl::Error map(GLenum access, GLvoid **mapPtr) override;
gl::Error mapRange(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr) override;
gl::Error unmap(GLboolean *result) override;
gl::Error markTransformFeedbackUsage() override;
// We use two set of dirty callbacks for two events. Static buffers are marked dirty whenever
// the data is changed, because they must be re-translated. Direct buffers only need to be
// updated when the underlying ID3D11Buffer pointer changes - hopefully far less often.
void addStaticBufferDirtyCallback(const NotificationCallback *callback);
void removeStaticBufferDirtyCallback(const NotificationCallback *callback);
void addDirectBufferDirtyCallback(const NotificationCallback *callback);
void removeDirectBufferDirtyCallback(const NotificationCallback *callback);
private:
class BufferStorage;
......@@ -94,13 +109,6 @@ class Buffer11 : public BufferD3D
class PackStorage;
class SystemMemoryStorage;
Renderer11 *mRenderer;
size_t mSize;
BufferStorage *mMappedStorage;
std::vector<BufferStorage*> mBufferStorages;
struct ConstantBufferCacheEntry
{
ConstantBufferCacheEntry() : storage(nullptr), lruCount(0) { }
......@@ -109,6 +117,27 @@ class Buffer11 : public BufferD3D
unsigned int lruCount;
};
gl::Error markBufferUsage();
gl::ErrorOrResult<NativeStorage *> getStagingStorage();
gl::ErrorOrResult<PackStorage *> getPackStorage();
gl::ErrorOrResult<SystemMemoryStorage *> getSystemMemoryStorage();
gl::Error updateBufferStorage(BufferStorage *storage, size_t sourceOffset, size_t storageSize);
gl::ErrorOrResult<BufferStorage *> getBufferStorage(BufferUsage usage);
gl::ErrorOrResult<BufferStorage *> getLatestBufferStorage() const;
gl::ErrorOrResult<BufferStorage *> getConstantBufferRangeStorage(GLintptr offset,
GLsizeiptr size);
BufferStorage *allocateStorage(BufferUsage usage) const;
Renderer11 *mRenderer;
size_t mSize;
BufferStorage *mMappedStorage;
std::vector<BufferStorage *> mBufferStorages;
// Cache of D3D11 constant buffer for specific ranges of buffer data.
// This is used to emulate UBO ranges on 11.0 devices.
// Constant buffers are indexed by there start offset.
......@@ -122,17 +151,8 @@ class Buffer11 : public BufferD3D
unsigned int mReadUsageCount;
gl::Error markBufferUsage();
gl::ErrorOrResult<NativeStorage *> getStagingStorage();
gl::ErrorOrResult<PackStorage *> getPackStorage();
gl::ErrorOrResult<SystemMemoryStorage *> getSystemMemoryStorage();
gl::Error updateBufferStorage(BufferStorage *storage, size_t sourceOffset, size_t storageSize);
gl::ErrorOrResult<BufferStorage *> getBufferStorage(BufferUsage usage);
gl::ErrorOrResult<BufferStorage *> getLatestBufferStorage() const;
gl::ErrorOrResult<BufferStorage *> getConstantBufferRangeStorage(GLintptr offset,
GLsizeiptr size);
NotificationSet mStaticBufferDirtyCallbacks;
NotificationSet mDirectBufferDirtyCallbacks;
};
} // namespace rx
......
......@@ -94,7 +94,8 @@ Optional<size_t> FindFirstNonInstanced(
}
void SortAttributesByLayout(const gl::Program *program,
const std::vector<TranslatedAttribute> &unsortedAttributes,
const std::vector<TranslatedAttribute> &vertexArrayAttribs,
const std::vector<TranslatedAttribute> &currentValueAttribs,
AttribIndexArray *sortedD3DSemanticsOut,
std::vector<const TranslatedAttribute *> *sortedAttributesOut)
{
......@@ -112,7 +113,17 @@ void SortAttributesByLayout(const gl::Program *program,
}
(*sortedD3DSemanticsOut)[d3dSemantic] = d3dSemantic;
(*sortedAttributesOut)[d3dSemantic] = &unsortedAttributes[locationIndex];
const auto *arrayAttrib = &vertexArrayAttribs[locationIndex];
if (arrayAttrib->attribute && arrayAttrib->attribute->enabled)
{
(*sortedAttributesOut)[d3dSemantic] = arrayAttrib;
}
else
{
ASSERT(currentValueAttribs[locationIndex].attribute);
(*sortedAttributesOut)[d3dSemantic] = &currentValueAttribs[locationIndex];
}
}
}
......@@ -209,8 +220,10 @@ void InputLayoutCache::markDirty()
gl::Error InputLayoutCache::applyVertexBuffers(
const gl::State &state,
const std::vector<TranslatedAttribute> &unsortedAttributes,
const std::vector<TranslatedAttribute> &vertexArrayAttribs,
const std::vector<TranslatedAttribute> &currentValueAttribs,
GLenum mode,
GLint start,
TranslatedIndexData *indexInfo,
GLsizei numIndicesPerInstance)
{
......@@ -223,7 +236,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(
bool instancedPointSpritesActive = programUsesInstancedPointSprites && (mode == GL_POINTS);
AttribIndexArray sortedSemanticIndices;
SortAttributesByLayout(program, unsortedAttributes, &sortedSemanticIndices,
SortAttributesByLayout(program, vertexArrayAttribs, currentValueAttribs, &sortedSemanticIndices,
&mCurrentAttributes);
// If we are using FL 9_3, make sure the first attribute is not instanced
......@@ -285,9 +298,9 @@ gl::Error InputLayoutCache::applyVertexBuffers(
indexInfo->srcIndexData.srcIndices = bufferData + offset;
}
ANGLE_TRY_RESULT(
bufferStorage->getEmulatedIndexedBuffer(&indexInfo->srcIndexData, attrib),
buffer);
ANGLE_TRY_RESULT(bufferStorage->getEmulatedIndexedBuffer(&indexInfo->srcIndexData,
attrib, start),
buffer);
}
else
{
......@@ -296,7 +309,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(
}
vertexStride = attrib.stride;
vertexOffset = attrib.offset;
ANGLE_TRY_RESULT(attrib.computeOffset(start), vertexOffset);
}
size_t bufferIndex = reservedBuffers + attribIndex;
......@@ -414,7 +427,8 @@ gl::Error InputLayoutCache::applyVertexBuffers(
return gl::NoError();
}
gl::Error InputLayoutCache::updateVertexOffsetsForPointSpritesEmulation(GLsizei emulatedInstanceId)
gl::Error InputLayoutCache::updateVertexOffsetsForPointSpritesEmulation(GLint startVertex,
GLsizei emulatedInstanceId)
{
size_t reservedBuffers = GetReservedBufferCount(true);
for (size_t attribIndex = 0; attribIndex < mCurrentAttributes.size(); ++attribIndex)
......@@ -424,8 +438,10 @@ gl::Error InputLayoutCache::updateVertexOffsetsForPointSpritesEmulation(GLsizei
if (attrib.divisor > 0)
{
unsigned int offset = 0;
ANGLE_TRY_RESULT(attrib.computeOffset(startVertex), offset);
mCurrentVertexOffsets[bufferIndex] =
attrib.offset + (attrib.stride * (emulatedInstanceId / attrib.divisor));
offset + (attrib.stride * (emulatedInstanceId / attrib.divisor));
}
}
......
......@@ -46,12 +46,15 @@ class InputLayoutCache : angle::NonCopyable
void markDirty();
gl::Error applyVertexBuffers(const gl::State &state,
const std::vector<TranslatedAttribute> &attributes,
const std::vector<TranslatedAttribute> &vertexArrayAttribs,
const std::vector<TranslatedAttribute> &currentValueAttribs,
GLenum mode,
GLint start,
TranslatedIndexData *indexInfo,
GLsizei numIndicesPerInstance);
gl::Error updateVertexOffsetsForPointSpritesEmulation(GLsizei emulatedInstanceId);
gl::Error updateVertexOffsetsForPointSpritesEmulation(GLint startVertex,
GLsizei emulatedInstanceId);
// Useful for testing
void setCacheSize(unsigned int cacheSize) { mCacheSize = cacheSize; }
......
......@@ -189,23 +189,17 @@ RenderTarget11::~RenderTarget11()
void RenderTarget11::addDirtyCallback(const NotificationCallback *callback)
{
mDirtyCallbacks.insert(callback);
mDirtyCallbacks.add(callback);
}
void RenderTarget11::removeDirtyCallback(const NotificationCallback *callback)
{
mDirtyCallbacks.erase(callback);
mDirtyCallbacks.remove(callback);
}
void RenderTarget11::signalDirty()
{
if (mDirtyCallbacks.empty())
return;
for (const auto &callback : mDirtyCallbacks)
{
(*callback)();
}
mDirtyCallbacks.signal();
// Clear the signal list. We can't do this in the callback because it mutates the iterator.
mDirtyCallbacks.clear();
......
......@@ -41,8 +41,7 @@ class RenderTarget11 : public RenderTargetD3D
d3d11::ANGLEFormat getANGLEFormat() const { return mANGLEFormat; }
protected:
std::set<const NotificationCallback *> mDirtyCallbacks;
NotificationSet mDirtyCallbacks;
d3d11::ANGLEFormat mANGLEFormat;
};
......
......@@ -835,9 +835,6 @@ void Renderer11::initializeDevice()
ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.D3D11FeatureLevel",
angleFeatureLevel,
NUM_ANGLE_FEATURE_LEVELS);
// TODO(jmadill): use context caps, and place in common D3D location
mTranslatedAttribCache.resize(getRendererCaps().maxVertexAttributes);
}
void Renderer11::populateRenderer11DeviceCaps()
......@@ -1524,7 +1521,17 @@ gl::Error Renderer11::applyVertexBuffer(const gl::State &state,
GLsizei instances,
TranslatedIndexData *indexInfo)
{
gl::Error error = mVertexDataManager->prepareVertexData(state, first, count, &mTranslatedAttribCache, instances);
const auto &vertexArray = state.getVertexArray();
auto *vertexArray11 = GetImplAs<VertexArray11>(vertexArray);
gl::Error error = vertexArray11->updateDirtyAndDynamicAttribs(mVertexDataManager, state, first,
count, instances);
if (error.isError())
{
return error;
}
error = mStateManager.updateCurrentValueAttribs(state, mVertexDataManager);
if (error.isError())
{
return error;
......@@ -1541,8 +1548,21 @@ gl::Error Renderer11::applyVertexBuffer(const gl::State &state,
{
numIndicesPerInstance = count;
}
return mInputLayoutCache.applyVertexBuffers(state, mTranslatedAttribCache, mode, indexInfo,
numIndicesPerInstance);
const auto &vertexArrayAttribs = vertexArray11->getTranslatedAttribs();
const auto &currentValueAttribs = mStateManager.getCurrentValueAttribs();
ANGLE_TRY(mInputLayoutCache.applyVertexBuffers(state, vertexArrayAttribs, currentValueAttribs,
mode, first, indexInfo, numIndicesPerInstance));
// InputLayoutCache::applyVertexBuffers calls through to the Bufer11 to get the native vertex
// buffer (ID3D11Buffer *). Because we allocate these buffers lazily, this will trigger
// allocation. This in turn will signal that the buffer is dirty. Since we just resolved the
// dirty-ness in VertexArray11::updateDirtyAndDynamicAttribs, this can make us do a needless
// update on the second draw call.
// Hence we clear the flags here, after we've applied vertex data, since we know everything
// is clean. This is a bit of a hack.
vertexArray11->clearDirtyAndPromoteDynamicAttribs(state, count);
return gl::NoError();
}
gl::Error Renderer11::applyIndexBuffer(const gl::Data &data,
......@@ -1669,6 +1689,7 @@ gl::Error Renderer11::applyTransformFeedbackBuffers(const gl::State &state)
gl::Error Renderer11::drawArraysImpl(const gl::Data &data,
GLenum mode,
GLint startVertex,
GLsizei count,
GLsizei instances)
{
......@@ -1761,7 +1782,8 @@ gl::Error Renderer11::drawArraysImpl(const gl::Data &data,
// offsets.
for (GLsizei i = 0; i < instances; i++)
{
gl::Error error = mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(i);
gl::Error error =
mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(startVertex, i);
if (error.isError())
{
return error;
......@@ -1827,7 +1849,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Data &data,
// offsets.
for (GLsizei i = 0; i < instances; i++)
{
gl::Error error = mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(i);
gl::Error error =
mInputLayoutCache.updateVertexOffsetsForPointSpritesEmulation(minIndex, i);
if (error.isError())
{
return error;
......@@ -2505,6 +2528,7 @@ void Renderer11::markAllStateDirty()
void Renderer11::releaseDeviceResources()
{
mStateManager.deinitialize();
mStateCache.clear();
mInputLayoutCache.clear();
......@@ -4340,4 +4364,5 @@ egl::Error Renderer11::getEGLDevice(DeviceImpl **device)
*device = static_cast<DeviceImpl *>(mEGLDevice);
return egl::Error(EGL_SUCCESS);
}
}
} // namespace rx
......@@ -306,6 +306,7 @@ class Renderer11 : public RendererD3D
private:
gl::Error drawArraysImpl(const gl::Data &data,
GLenum mode,
GLint startVertex,
GLsizei count,
GLsizei instances) override;
gl::Error drawElementsImpl(const gl::Data &data,
......
......@@ -150,7 +150,9 @@ StateManager11::StateManager11(Renderer11 *renderer)
mCurNear(0.0f),
mCurFar(0.0f),
mViewportBounds(),
mRenderTargetIsDirty(false)
mRenderTargetIsDirty(false),
mDirtyCurrentValueAttribs(),
mCurrentValueAttribs()
{
mCurBlendState.blend = false;
mCurBlendState.sourceBlendRGB = GL_ONE;
......@@ -191,6 +193,9 @@ StateManager11::StateManager11(Renderer11 *renderer)
mCurRasterState.polygonOffsetUnits = 0.0f;
mCurRasterState.pointDrawMode = false;
mCurRasterState.multiSample = false;
// Initially all current value attributes must be updated on first use.
mDirtyCurrentValueAttribs.flip();
}
StateManager11::~StateManager11()
......@@ -241,7 +246,7 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit
return;
}
for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits))
for (auto dirtyBit : angle::IterateBitSet(dirtyBits))
{
switch (dirtyBit)
{
......@@ -461,6 +466,13 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit
mRenderTargetIsDirty = true;
break;
default:
if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
dirtyBit < gl::State::DIRTY_BIT_CURRENT_VALUE_MAX)
{
size_t attribIndex =
static_cast<size_t>(dirtyBit - gl::State::DIRTY_BIT_CURRENT_VALUE_0);
mDirtyCurrentValueAttribs.set(attribIndex);
}
break;
}
}
......@@ -958,6 +970,13 @@ void StateManager11::initialize(const gl::Caps &caps)
// Initialize cached NULL SRV block
mNullSRVs.resize(caps.maxTextureImageUnits, nullptr);
mCurrentValueAttribs.resize(caps.maxVertexAttributes);
}
void StateManager11::deinitialize()
{
mCurrentValueAttribs.clear();
}
gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer)
......@@ -1078,4 +1097,40 @@ gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer)
return gl::Error(GL_NO_ERROR);
}
gl::Error StateManager11::updateCurrentValueAttribs(const gl::State &state,
VertexDataManager *vertexDataManager)
{
const auto &activeAttribsMask = state.getProgram()->getActiveAttribLocationsMask();
const auto &dirtyActiveAttribs = (activeAttribsMask & mDirtyCurrentValueAttribs);
const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes();
for (auto attribIndex : angle::IterateBitSet(dirtyActiveAttribs))
{
if (vertexAttributes[attribIndex].enabled)
continue;
mDirtyCurrentValueAttribs.reset(attribIndex);
const auto &currentValue =
state.getVertexAttribCurrentValue(static_cast<unsigned int>(attribIndex));
auto currentValueAttrib = &mCurrentValueAttribs[attribIndex];
currentValueAttrib->currentValueType = currentValue.Type;
currentValueAttrib->attribute = &vertexAttributes[attribIndex];
gl::Error error = vertexDataManager->storeCurrentValue(currentValue, currentValueAttrib,
static_cast<size_t>(attribIndex));
if (error.isError())
{
return error;
}
}
return gl::Error(GL_NO_ERROR);
}
const std::vector<TranslatedAttribute> &StateManager11::getCurrentValueAttribs() const
{
return mCurrentValueAttribs;
}
} // namespace rx
......@@ -48,6 +48,7 @@ class StateManager11 final : angle::NonCopyable
~StateManager11();
void initialize(const gl::Caps &caps);
void deinitialize();
void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits);
gl::Error setBlendState(const gl::Framebuffer *framebuffer,
......@@ -91,6 +92,11 @@ class StateManager11 final : angle::NonCopyable
void onDeleteQueryObject(Query11 *query);
gl::Error onMakeCurrent(const gl::Data &data);
gl::Error updateCurrentValueAttribs(const gl::State &state,
VertexDataManager *vertexDataManager);
const std::vector<TranslatedAttribute> &getCurrentValueAttribs() const;
private:
void setViewportBounds(const int width, const int height);
void unsetConflictingSRVs(gl::SamplerType shaderType,
......@@ -186,6 +192,10 @@ class StateManager11 final : angle::NonCopyable
// A block of NULL pointers, cached so we don't re-allocate every draw call
std::vector<ID3D11ShaderResourceView *> mNullSRVs;
// Current translations of "Current-Value" data - owned by Context, not VertexArray.
gl::AttributesMask mDirtyCurrentValueAttribs;
std::vector<TranslatedAttribute> mCurrentValueAttribs;
};
} // namespace rx
......
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VertexArray11:
// Implementation of rx::VertexArray11.
//
#include "libANGLE/renderer/d3d/d3d11/VertexArray11.h"
#include "common/BitSetIterator.h"
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
namespace rx
{
namespace
{
size_t GetAttribIndex(unsigned long dirtyBit)
{
if (dirtyBit >= gl::VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED &&
dirtyBit < gl::VertexArray::DIRTY_BIT_ATTRIB_MAX_ENABLED)
{
return dirtyBit - gl::VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED;
}
if (dirtyBit >= gl::VertexArray::DIRTY_BIT_ATTRIB_0_POINTER &&
dirtyBit < gl::VertexArray::DIRTY_BIT_ATTRIB_MAX_POINTER)
{
return dirtyBit - gl::VertexArray::DIRTY_BIT_ATTRIB_0_POINTER;
}
ASSERT(dirtyBit >= gl::VertexArray::DIRTY_BIT_ATTRIB_0_DIVISOR &&
dirtyBit < gl::VertexArray::DIRTY_BIT_ATTRIB_MAX_DIVISOR);
return static_cast<size_t>(dirtyBit) - gl::VertexArray::DIRTY_BIT_ATTRIB_0_DIVISOR;
}
} // anonymous namespace
VertexArray11::VertexArray11(const gl::VertexArray::Data &data)
: VertexArrayImpl(data),
mAttributeStorageTypes(data.getVertexAttributes().size(), VertexStorageType::CURRENT_VALUE),
mTranslatedAttribs(data.getVertexAttributes().size()),
mCurrentBuffers(data.getVertexAttributes().size())
{
for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex)
{
auto callback = [this, attribIndex]()
{
this->markBufferDataDirty(attribIndex);
};
mOnBufferDataDirty.push_back(callback);
}
}
VertexArray11::~VertexArray11()
{
for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex)
{
if (mCurrentBuffers[attribIndex].get())
{
unlinkBuffer(attribIndex, mAttributeStorageTypes[attribIndex]);
mCurrentBuffers[attribIndex].set(nullptr);
}
}
}
void VertexArray11::syncState(const gl::VertexArray::DirtyBits &dirtyBits)
{
for (auto dirtyBit : angle::IterateBitSet(dirtyBits))
{
if (dirtyBit == gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER)
continue;
size_t attribIndex = GetAttribIndex(dirtyBit);
mAttribsToUpdate.set(attribIndex);
}
}
void VertexArray11::updateVertexAttribStorage(size_t attribIndex)
{
const auto &attrib = mData.getVertexAttribute(attribIndex);
// Note: having an unchanged storage type doesn't mean the attribute is clean.
auto oldStorageType = mAttributeStorageTypes[attribIndex];
auto newStorageType = ClassifyAttributeStorage(attrib);
mAttributeStorageTypes[attribIndex] = newStorageType;
if (newStorageType == VertexStorageType::DYNAMIC)
{
if (oldStorageType != VertexStorageType::DYNAMIC)
{
// Sync dynamic attribs in a different set.
mAttribsToTranslate.reset(attribIndex);
mDynamicAttribsMask.set(attribIndex);
}
}
else
{
mAttribsToTranslate.set(attribIndex);
if (oldStorageType == VertexStorageType::DYNAMIC)
{
ASSERT(mDynamicAttribsMask[attribIndex]);
mDynamicAttribsMask.reset(attribIndex);
}
}
gl::Buffer *oldBufferGL = mCurrentBuffers[attribIndex].get();
gl::Buffer *newBufferGL = attrib.buffer.get();
Buffer11 *oldBuffer11 = oldBufferGL ? GetImplAs<Buffer11>(oldBufferGL) : nullptr;
Buffer11 *newBuffer11 = newBufferGL ? GetImplAs<Buffer11>(newBufferGL) : nullptr;
if (oldBuffer11 != newBuffer11 || oldStorageType != newStorageType)
{
// Note that for static callbacks, promotion to a static buffer from a dynamic buffer means
// we need to tag dynamic buffers with static callbacks.
if (oldBuffer11 != nullptr)
{
unlinkBuffer(attribIndex, oldStorageType);
}
if (newBuffer11 != nullptr)
{
if (newStorageType == VertexStorageType::DIRECT)
{
newBuffer11->addDirectBufferDirtyCallback(&mOnBufferDataDirty[attribIndex]);
}
else if (newStorageType == VertexStorageType::STATIC ||
newStorageType == VertexStorageType::DYNAMIC)
{
newBuffer11->addStaticBufferDirtyCallback(&mOnBufferDataDirty[attribIndex]);
}
}
mCurrentBuffers[attribIndex] = attrib.buffer;
}
}
gl::Error VertexArray11::updateDirtyAndDynamicAttribs(VertexDataManager *vertexDataManager,
const gl::State &state,
GLint start,
GLsizei count,
GLsizei instances)
{
const gl::Program *program = state.getProgram();
const auto &activeLocations = program->getActiveAttribLocationsMask();
if (mAttribsToUpdate.any())
{
// Skip attrib locations the program doesn't use.
const auto &activeToUpdate = (mAttribsToUpdate & activeLocations);
for (auto toUpdateIndex : angle::IterateBitSet(activeToUpdate))
{
mAttribsToUpdate.reset(toUpdateIndex);
updateVertexAttribStorage(toUpdateIndex);
}
}
const auto &attribs = mData.getVertexAttributes();
if (mAttribsToTranslate.any())
{
// Skip attrib locations the program doesn't use, saving for the next frame.
const auto &dirtyActiveAttribs = (mAttribsToTranslate & activeLocations);
for (auto dirtyAttribIndex : angle::IterateBitSet(dirtyActiveAttribs))
{
mAttribsToTranslate.reset(dirtyAttribIndex);
auto *translatedAttrib = &mTranslatedAttribs[dirtyAttribIndex];
const auto &currentValue =
state.getVertexAttribCurrentValue(static_cast<unsigned int>(dirtyAttribIndex));
// Record basic attrib info
translatedAttrib->attribute = &attribs[dirtyAttribIndex];
translatedAttrib->currentValueType = currentValue.Type;
translatedAttrib->divisor = translatedAttrib->attribute->divisor;
switch (mAttributeStorageTypes[dirtyAttribIndex])
{
case VertexStorageType::DIRECT:
VertexDataManager::StoreDirectAttrib(translatedAttrib);
break;
case VertexStorageType::STATIC:
{
auto error =
VertexDataManager::StoreStaticAttrib(translatedAttrib, count, instances);
if (error.isError())
{
return error;
}
break;
}
case VertexStorageType::CURRENT_VALUE:
// Current value attribs are managed by the StateManager11.
break;
default:
UNREACHABLE();
break;
}
}
}
if (mDynamicAttribsMask.any())
{
auto activeDynamicAttribs = (mDynamicAttribsMask & activeLocations);
for (auto dynamicAttribIndex : angle::IterateBitSet(activeDynamicAttribs))
{
auto *dynamicAttrib = &mTranslatedAttribs[dynamicAttribIndex];
const auto &currentValue =
state.getVertexAttribCurrentValue(static_cast<unsigned int>(dynamicAttribIndex));
// Record basic attrib info
dynamicAttrib->attribute = &attribs[dynamicAttribIndex];
dynamicAttrib->currentValueType = currentValue.Type;
dynamicAttrib->divisor = dynamicAttrib->attribute->divisor;
}
return vertexDataManager->storeDynamicAttribs(&mTranslatedAttribs, activeDynamicAttribs,
start, count, instances);
}
return gl::Error(GL_NO_ERROR);
}
const std::vector<TranslatedAttribute> &VertexArray11::getTranslatedAttribs() const
{
return mTranslatedAttribs;
}
void VertexArray11::markBufferDataDirty(size_t attribIndex)
{
ASSERT(mAttributeStorageTypes[attribIndex] != VertexStorageType::CURRENT_VALUE);
// This can change a buffer's storage, we'll need to re-check.
mAttribsToUpdate.set(attribIndex);
}
void VertexArray11::clearDirtyAndPromoteDynamicAttribs(const gl::State &state, GLsizei count)
{
const gl::Program *program = state.getProgram();
const auto &activeLocations = program->getActiveAttribLocationsMask();
mAttribsToUpdate &= ~activeLocations;
// Promote to static after we clear the dirty attributes, otherwise we can lose dirtyness.
auto activeDynamicAttribs = (mDynamicAttribsMask & activeLocations);
VertexDataManager::PromoteDynamicAttribs(mTranslatedAttribs, activeDynamicAttribs, count);
}
void VertexArray11::unlinkBuffer(size_t attribIndex, VertexStorageType storageType)
{
Buffer11 *buffer = GetImplAs<Buffer11>(mCurrentBuffers[attribIndex].get());
if (storageType == VertexStorageType::DIRECT)
{
buffer->removeDirectBufferDirtyCallback(&mOnBufferDataDirty[attribIndex]);
}
else if (storageType == VertexStorageType::STATIC || storageType == VertexStorageType::DYNAMIC)
{
buffer->removeStaticBufferDirtyCallback(&mOnBufferDataDirty[attribIndex]);
}
}
} // namespace rx
......@@ -19,13 +19,42 @@ class Renderer11;
class VertexArray11 : public VertexArrayImpl
{
public:
VertexArray11(const gl::VertexArray::Data &data)
: VertexArrayImpl(data)
{
}
virtual ~VertexArray11() {}
VertexArray11(const gl::VertexArray::Data &data);
~VertexArray11() override;
void syncState(const gl::VertexArray::DirtyBits &dirtyBits) override;
gl::Error updateDirtyAndDynamicAttribs(VertexDataManager *vertexDataManager,
const gl::State &state,
GLint start,
GLsizei count,
GLsizei instances);
void clearDirtyAndPromoteDynamicAttribs(const gl::State &state, GLsizei count);
const std::vector<TranslatedAttribute> &getTranslatedAttribs() const;
private:
void updateVertexAttribStorage(size_t attribIndex);
void markBufferDataDirty(size_t attribIndex);
void unlinkBuffer(size_t attribIndex, VertexStorageType storageType);
std::vector<VertexStorageType> mAttributeStorageTypes;
std::vector<TranslatedAttribute> mTranslatedAttribs;
// The mask of attributes marked as dynamic.
gl::AttributesMask mDynamicAttribsMask;
// A mask of attributes that need to be re-evaluated.
gl::AttributesMask mAttribsToUpdate;
// A set of attributes we know are dirty, and need to be re-translated.
gl::AttributesMask mAttribsToTranslate;
// We need to keep a safe pointer to the Buffer so we can attach the correct dirty callbacks.
std::vector<BindingPointer<gl::Buffer>> mCurrentBuffers;
std::vector<NotificationCallback> mOnBufferDataDirty;
};
}
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_D3D11_VERTEXARRAY11_H_
......@@ -1716,4 +1716,40 @@ bool UsePresentPathFast(const Renderer11 *renderer,
renderer->presentPathFastEnabled());
}
NotificationSet::NotificationSet()
{
}
NotificationSet::~NotificationSet()
{
}
void NotificationSet::add(const NotificationCallback *callback)
{
ASSERT(mCallbacks.count(callback) == 0);
mCallbacks.insert(callback);
}
void NotificationSet::remove(const NotificationCallback *callback)
{
ASSERT(mCallbacks.count(callback) == 1);
mCallbacks.erase(callback);
}
void NotificationSet::signal() const
{
if (mCallbacks.empty())
return;
for (const auto *callback : mCallbacks)
{
(*callback)();
}
}
void NotificationSet::clear()
{
mCallbacks.clear();
}
} // namespace rx
......@@ -406,6 +406,21 @@ bool UsePresentPathFast(const Renderer11 *renderer, const gl::FramebufferAttachm
using NotificationCallback = std::function<void()>;
class NotificationSet final : angle::NonCopyable
{
public:
NotificationSet();
~NotificationSet();
void add(const NotificationCallback *callback);
void remove(const NotificationCallback *callback);
void signal() const;
void clear();
private:
std::set<const NotificationCallback *> mCallbacks;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
......@@ -154,6 +154,8 @@ void Renderer9::release()
{
RendererD3D::cleanup();
mTranslatedAttribCache.clear();
releaseDeviceResources();
SafeDelete(mEGLDevice);
......@@ -364,7 +366,6 @@ void Renderer9::initializeDevice()
mVertexDataManager = new VertexDataManager(this);
mIndexDataManager = new IndexDataManager(this, getRendererClass());
// TODO(jmadill): use context caps, and place in common D3D location
mTranslatedAttribCache.resize(getRendererCaps().maxVertexAttributes);
}
......@@ -1191,7 +1192,8 @@ gl::Error Renderer9::applyVertexBuffer(const gl::State &state,
return error;
}
return mVertexDeclarationCache.applyDeclaration(mDevice, mTranslatedAttribCache, state.getProgram(), instances, &mRepeatDraw);
return mVertexDeclarationCache.applyDeclaration(
mDevice, mTranslatedAttribCache, state.getProgram(), first, instances, &mRepeatDraw);
}
// Applies the indices and element array bindings to the Direct3D 9 device
......@@ -1233,6 +1235,7 @@ gl::Error Renderer9::applyTransformFeedbackBuffers(const gl::State &state)
gl::Error Renderer9::drawArraysImpl(const gl::Data &data,
GLenum mode,
GLint startVertex,
GLsizei count,
GLsizei instances)
{
......
......@@ -275,6 +275,7 @@ class Renderer9 : public RendererD3D
private:
gl::Error drawArraysImpl(const gl::Data &data,
GLenum mode,
GLint startVertex,
GLsizei count,
GLsizei instances) override;
gl::Error drawElementsImpl(const gl::Data &data,
......@@ -396,6 +397,7 @@ class Renderer9 : public RendererD3D
UINT mMaxNullColorbufferLRU;
DeviceD3D *mEGLDevice;
std::vector<TranslatedAttribute> mTranslatedAttribCache;
};
}
......
......@@ -42,11 +42,13 @@ VertexDeclarationCache::~VertexDeclarationCache()
}
}
gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device,
const std::vector<TranslatedAttribute> &attributes,
gl::Program *program,
GLsizei instances,
GLsizei *repeatDraw)
gl::Error VertexDeclarationCache::applyDeclaration(
IDirect3DDevice9 *device,
const std::vector<TranslatedAttribute> &attributes,
gl::Program *program,
GLint start,
GLsizei instances,
GLsizei *repeatDraw)
{
ASSERT(gl::MAX_VERTEX_ATTRIBS >= attributes.size());
......@@ -149,14 +151,18 @@ gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device,
VertexBuffer9 *vertexBuffer = GetAs<VertexBuffer9>(attributes[i].vertexBuffer.get());
unsigned int offset = 0;
ANGLE_TRY_RESULT(attributes[i].computeOffset(start), offset);
if (mAppliedVBs[stream].serial != attributes[i].serial ||
mAppliedVBs[stream].stride != attributes[i].stride ||
mAppliedVBs[stream].offset != attributes[i].offset)
mAppliedVBs[stream].offset != offset)
{
device->SetStreamSource(stream, vertexBuffer->getBuffer(), attributes[i].offset, attributes[i].stride);
device->SetStreamSource(stream, vertexBuffer->getBuffer(), offset,
attributes[i].stride);
mAppliedVBs[stream].serial = attributes[i].serial;
mAppliedVBs[stream].stride = attributes[i].stride;
mAppliedVBs[stream].offset = attributes[i].offset;
mAppliedVBs[stream].offset = offset;
}
gl::VertexFormatType vertexformatType = gl::GetVertexFormatType(*attributes[i].attribute, GL_FLOAT);
......
......@@ -30,6 +30,7 @@ class VertexDeclarationCache
gl::Error applyDeclaration(IDirect3DDevice9 *device,
const std::vector<TranslatedAttribute> &attributes,
gl::Program *program,
GLint start,
GLsizei instances,
GLsizei *repeatDraw);
......
......@@ -392,6 +392,7 @@
'libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp',
'libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.h',
'libANGLE/renderer/d3d/d3d11/texture_format_table.h',
'libANGLE/renderer/d3d/d3d11/VertexArray11.cpp',
'libANGLE/renderer/d3d/d3d11/VertexArray11.h',
'libANGLE/renderer/d3d/d3d11/VertexBuffer11.cpp',
'libANGLE/renderer/d3d/d3d11/VertexBuffer11.h',
......
......@@ -207,6 +207,31 @@ TEST_P(BufferDataTest, DISABLED_HugeSetDataShouldNotCrash)
delete[] data;
}
// Internally in D3D, we promote dynamic data to static after many draw loops. This code tests
// path.
TEST_P(BufferDataTest, RepeatedDrawWithDynamic)
{
std::vector<GLfloat> data;
for (int i = 0; i < 16; ++i)
{
data.push_back(static_cast<GLfloat>(i));
}
glUseProgram(mProgram);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(mAttribLocation, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(mAttribLocation);
for (int drawCount = 0; drawCount < 40; ++drawCount)
{
drawQuad(mProgram, "position", 0.5f);
}
EXPECT_GL_NO_ERROR();
}
class IndexedBufferCopyTest : public ANGLETest
{
protected:
......
......@@ -82,7 +82,7 @@ class CopyTexImageTest : public ANGLETest
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, w, h);
}
virtual void SetUp()
void SetUp() override
{
ANGLETest::SetUp();
......@@ -119,14 +119,14 @@ class CopyTexImageTest : public ANGLETest
ASSERT_GL_NO_ERROR();
}
virtual void TearDown()
void TearDown() override
{
glDeleteProgram(mTextureProgram);
ANGLETest::TearDown();
}
void verifyResults(GLuint texture, GLubyte data[4], GLint x, GLint y) const
void verifyResults(GLuint texture, GLubyte data[4], GLint x, GLint y)
{
glViewport(0, 0, 16, 16);
......
......@@ -38,7 +38,8 @@ class D3D11EmulatedIndexedBufferTest : public ANGLETest
gl::Error error = mSourceBuffer->setData(testData, sizeof(testData), GL_STATIC_DRAW);
ASSERT_FALSE(error.isError());
mTranslatedAttribute.offset = 0;
mTranslatedAttribute.baseOffset = 0;
mTranslatedAttribute.usesFirstVertexOffset = false;
mTranslatedAttribute.stride = sizeof(GLfloat);
GLubyte indices[] = {0, 0, 3, 4, 2, 1, 1};
......@@ -107,7 +108,8 @@ class D3D11EmulatedIndexedBufferTest : public ANGLETest
void emulateAndCompare(rx::SourceIndexData *srcData)
{
auto bufferOrError = mSourceBuffer->getEmulatedIndexedBuffer(srcData, mTranslatedAttribute);
auto bufferOrError =
mSourceBuffer->getEmulatedIndexedBuffer(srcData, mTranslatedAttribute, 0);
ASSERT_FALSE(bufferOrError.isError());
ID3D11Buffer *emulatedBuffer = bufferOrError.getResult();
ASSERT_TRUE(emulatedBuffer != nullptr);
......
......@@ -707,7 +707,90 @@ TEST_P(TransformFeedbackTest, PackingBug)
ASSERT_GL_NO_ERROR();
}
class TransformFeedbackLifetimeTest : public TransformFeedbackTest
{
protected:
TransformFeedbackLifetimeTest() : mVertexArray(0) {}
void SetUp() override
{
ANGLETest::SetUp();
glGenVertexArrays(1, &mVertexArray);
glBindVertexArray(mVertexArray);
std::vector<std::string> tfVaryings;
tfVaryings.push_back("gl_Position");
compileDefaultProgram(tfVaryings, GL_SEPARATE_ATTRIBS);
glGenBuffers(1, &mTransformFeedbackBuffer);
mTransformFeedbackBufferSize = 1 << 24; // ~16MB
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, NULL,
GL_DYNAMIC_DRAW);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
glGenTransformFeedbacks(1, &mTransformFeedback);
ASSERT_GL_NO_ERROR();
}
void TearDown() override
{
glDeleteVertexArrays(1, &mVertexArray);
TransformFeedbackTest::TearDown();
}
GLuint mVertexArray;
};
// Tests a bug with state syncing and deleted transform feedback buffers.
TEST_P(TransformFeedbackLifetimeTest, DeletedBuffer)
{
// First stream vertex data to mTransformFeedbackBuffer.
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
glUseProgram(mProgram);
glBeginTransformFeedback(GL_TRIANGLES);
drawQuad(mProgram, "position", 0.5f, 1.0f, true);
glEndTransformFeedback();
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
// TODO(jmadill): Remove this when http://anglebug.com/1351 is fixed.
glBindVertexArray(0);
drawQuad(mProgram, "position", 0.5f);
glBindVertexArray(1);
// Next, draw vertices with mTransformFeedbackBuffer. This will link to mVertexArray.
glBindBuffer(GL_ARRAY_BUFFER, mTransformFeedbackBuffer);
GLint loc = glGetAttribLocation(mProgram, "position");
ASSERT_NE(-1, loc);
glVertexAttribPointer(loc, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
glEnableVertexAttribArray(loc);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Delete resources, making a stranded pointer to mVertexArray in mTransformFeedbackBuffer.
glDeleteBuffers(1, &mTransformFeedbackBuffer);
mTransformFeedbackBuffer = 0;
glDeleteVertexArrays(1, &mVertexArray);
mVertexArray = 0;
// Then draw again with transform feedback, dereferencing the stranded pointer.
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
glBeginTransformFeedback(GL_TRIANGLES);
drawQuad(mProgram, "position", 0.5f, 1.0f, true);
glEndTransformFeedback();
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
ASSERT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(TransformFeedbackLifetimeTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
} // anonymous namespace
......@@ -50,7 +50,8 @@ GLfloat Normalize(T value)
class VertexAttributeTest : public ANGLETest
{
protected:
VertexAttributeTest() : mProgram(0), mTestAttrib(-1), mExpectedAttrib(-1), mBuffer(0)
VertexAttributeTest()
: mProgram(0), mTestAttrib(-1), mExpectedAttrib(-1), mBuffer(0), mQuadBuffer(0)
{
setWindowWidth(128);
setWindowHeight(128);
......@@ -122,6 +123,23 @@ class VertexAttributeTest : public ANGLETest
glEnableVertexAttribArray(mExpectedAttrib);
}
void checkPixels()
{
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
// We need to offset our checks from triangle edges to ensure we don't fall on a single tri
// Avoid making assumptions of drawQuad with four checks to check the four possible tri
// regions
EXPECT_PIXEL_EQ((midPixelX + viewportSize[0]) / 2, midPixelY, 255, 255, 255, 255);
EXPECT_PIXEL_EQ((midPixelX + viewportSize[2]) / 2, midPixelY, 255, 255, 255, 255);
EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[1]) / 2, 255, 255, 255, 255);
EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[3]) / 2, 255, 255, 255, 255);
}
void runTest(const TestData &test)
{
// TODO(geofflang): Figure out why this is broken on AMD OpenGL
......@@ -131,17 +149,6 @@ class VertexAttributeTest : public ANGLETest
return;
}
if (mProgram == 0)
{
initBasicProgram();
}
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
for (GLint i = 0; i < 4; i++)
{
GLint typeSize = i + 1;
......@@ -152,12 +159,7 @@ class VertexAttributeTest : public ANGLETest
glDisableVertexAttribArray(mTestAttrib);
glDisableVertexAttribArray(mExpectedAttrib);
// We need to offset our checks from triangle edges to ensure we don't fall on a single tri
// Avoid making assumptions of drawQuad with four checks to check the four possible tri regions
EXPECT_PIXEL_EQ((midPixelX + viewportSize[0]) / 2, midPixelY, 255, 255, 255, 255);
EXPECT_PIXEL_EQ((midPixelX + viewportSize[2]) / 2, midPixelY, 255, 255, 255, 255);
EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[1]) / 2, 255, 255, 255, 255);
EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[3]) / 2, 255, 255, 255, 255);
checkPixels();
}
}
......@@ -178,6 +180,7 @@ class VertexAttributeTest : public ANGLETest
{
glDeleteProgram(mProgram);
glDeleteBuffers(1, &mBuffer);
glDeleteBuffers(1, &mQuadBuffer);
ANGLETest::TearDown();
}
......@@ -262,6 +265,7 @@ class VertexAttributeTest : public ANGLETest
GLint mTestAttrib;
GLint mExpectedAttrib;
GLuint mBuffer;
GLuint mQuadBuffer;
};
TEST_P(VertexAttributeTest, UnsignedByteUnnormalized)
......@@ -545,6 +549,69 @@ TEST_P(VertexAttributeTest, DrawElementsBufferTooSmall)
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Verify that using a different start vertex doesn't mess up the draw.
TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset)
{
// TODO(jmadill): Diagnose this failure.
if (IsD3D11_FL93())
{
std::cout << "Test disabled on D3D11 FL 9_3" << std::endl;
return;
}
// TODO(geofflang): Figure out why this is broken on AMD OpenGL
if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
{
std::cout << "Test skipped on AMD OpenGL." << std::endl;
return;
}
initBasicProgram();
glUseProgram(mProgram);
GLfloat inputData[mVertexCount];
GLfloat expectedData[mVertexCount];
for (size_t count = 0; count < mVertexCount; ++count)
{
inputData[count] = static_cast<GLfloat>(count);
expectedData[count] = inputData[count];
}
auto quadVertices = GetQuadVertices();
GLsizei quadVerticesSize = static_cast<GLsizei>(quadVertices.size() * sizeof(quadVertices[0]));
glGenBuffers(1, &mQuadBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mQuadBuffer);
glBufferData(GL_ARRAY_BUFFER, quadVerticesSize + sizeof(Vector3), nullptr, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, quadVerticesSize, quadVertices.data());
GLint positionLocation = glGetAttribLocation(mProgram, "position");
ASSERT_NE(-1, positionLocation);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
GLsizei dataSize = mVertexCount * TypeStride(GL_FLOAT);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize + TypeStride(GL_FLOAT), nullptr, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, inputData);
glVertexAttribPointer(mTestAttrib, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(mTestAttrib);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(mExpectedAttrib, 1, GL_FLOAT, GL_FALSE, 0, expectedData);
glEnableVertexAttribArray(mExpectedAttrib);
// Vertex draw with no start vertex offset (second argument is zero).
glDrawArrays(GL_TRIANGLES, 0, 6);
checkPixels();
// Draw offset by one vertex.
glDrawArrays(GL_TRIANGLES, 1, 6);
checkPixels();
EXPECT_GL_NO_ERROR();
}
class VertexAttributeCachingTest : public VertexAttributeTest
{
protected:
......
......@@ -154,6 +154,39 @@ void ANGLETest::swapBuffers()
}
}
// static
std::array<Vector3, 6> ANGLETest::GetQuadVertices()
{
std::array<Vector3, 6> vertices;
vertices[0] = Vector3(-1.0f, 1.0f, 0.5f);
vertices[1] = Vector3(-1.0f, -1.0f, 0.5f);
vertices[2] = Vector3(1.0f, -1.0f, 0.5f);
vertices[3] = Vector3(-1.0f, 1.0f, 0.5f);
vertices[4] = Vector3(1.0f, -1.0f, 0.5f);
vertices[5] = Vector3(1.0f, 1.0f, 0.5f);
return vertices;
}
void ANGLETest::setupQuadVertexBuffer(GLfloat positionAttribZ, GLfloat positionAttribXYScale)
{
if (mQuadVertexBuffer == 0)
{
glGenBuffers(1, &mQuadVertexBuffer);
}
auto quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.x *= positionAttribXYScale;
vertex.y *= positionAttribXYScale;
vertex.z = positionAttribZ;
}
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
}
// static
void ANGLETest::drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ)
......@@ -161,11 +194,21 @@ void ANGLETest::drawQuad(GLuint program,
drawQuad(program, positionAttribName, positionAttribZ, 1.0f);
}
// static
void ANGLETest::drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale)
{
drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, false);
}
void ANGLETest::drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useVertexBuffer)
{
GLint previousProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram);
if (previousProgram != static_cast<GLint>(program))
......@@ -175,17 +218,24 @@ void ANGLETest::drawQuad(GLuint program,
GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
const GLfloat vertices[] = {
-1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
-1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
-1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
};
if (useVertexBuffer)
{
setupQuadVertexBuffer(positionAttribZ, positionAttribXYScale);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else
{
auto quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.x *= positionAttribXYScale;
vertex.y *= positionAttribXYScale;
vertex.z = positionAttribZ;
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
}
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
......@@ -213,27 +263,17 @@ void ANGLETest::drawIndexedQuad(GLuint program,
{
GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
glUseProgram(program);
GLint activeProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram);
if (static_cast<GLuint>(activeProgram) != program)
{
glUseProgram(program);
}
GLuint prevBinding = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, reinterpret_cast<GLint *>(&prevBinding));
if (mQuadVertexBuffer == 0)
{
glGenBuffers(1, &mQuadVertexBuffer);
const GLfloat vertices[] = {
-1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
-1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
};
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 4, vertices, GL_STATIC_DRAW);
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
}
setupQuadVertexBuffer(positionAttribZ, positionAttribXYScale);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
......@@ -248,7 +288,10 @@ void ANGLETest::drawIndexedQuad(GLuint program,
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glUseProgram(0);
if (static_cast<GLuint>(activeProgram) != program)
{
glUseProgram(static_cast<GLuint>(activeProgram));
}
}
GLuint ANGLETest::compileShader(GLenum type, const std::string &source)
......
......@@ -12,6 +12,7 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include "angle_gl.h"
#include "angle_test_configs.h"
......@@ -107,13 +108,19 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
virtual void swapBuffers();
static void drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ);
static void drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale);
void setupQuadVertexBuffer(GLfloat positionAttribZ, GLfloat positionAttribXYScale);
void drawQuad(GLuint program, const std::string &positionAttribName, GLfloat positionAttribZ);
void drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale);
void drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useVertexBuffer);
static std::array<Vector3, 6> GetQuadVertices();
void drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment