Commit 29639857 by Jamie Madill Committed by Commit Bot

D3D11: Work around Intel uniform buffers bug.

When copying from a staging buffer to a uniform buffer, the first upload would be incorrect. Work around this by trying to upload directly to a uniform buffer on the first BufferSubData call. BUG=chromium:593024 Change-Id: I0df3a1422b962bf3ece5d445f435df01e3544b67 Reviewed-on: https://chromium-review.googlesource.com/368774Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 4a9cd800
...@@ -44,45 +44,33 @@ const std::string &Buffer::getLabel() const ...@@ -44,45 +44,33 @@ const std::string &Buffer::getLabel() const
return mLabel; return mLabel;
} }
Error Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage) Error Buffer::bufferData(GLenum target, const void *data, GLsizeiptr size, GLenum usage)
{ {
gl::Error error = mBuffer->setData(data, size, usage); ANGLE_TRY(mBuffer->setData(target, data, size, usage));
if (error.isError())
{
return error;
}
mIndexRangeCache.clear(); mIndexRangeCache.clear();
mUsage = usage; mUsage = usage;
mSize = size; mSize = size;
return error; return NoError();
} }
Error Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset) Error Buffer::bufferSubData(GLenum target, const void *data, GLsizeiptr size, GLintptr offset)
{ {
gl::Error error = mBuffer->setSubData(data, size, offset); ANGLE_TRY(mBuffer->setSubData(target, data, size, offset));
if (error.isError())
{
return error;
}
mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset), static_cast<unsigned int>(size)); mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset), static_cast<unsigned int>(size));
return error; return NoError();
} }
Error Buffer::copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size) Error Buffer::copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size)
{ {
gl::Error error = mBuffer->copySubData(source->getImplementation(), sourceOffset, destOffset, size); ANGLE_TRY(mBuffer->copySubData(source->getImplementation(), sourceOffset, destOffset, size));
if (error.isError())
{
return error;
}
mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset), static_cast<unsigned int>(size)); mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset), static_cast<unsigned int>(size));
return error; return NoError();
} }
Error Buffer::map(GLenum access) Error Buffer::map(GLenum access)
...@@ -178,18 +166,14 @@ Error Buffer::getIndexRange(GLenum type, ...@@ -178,18 +166,14 @@ Error Buffer::getIndexRange(GLenum type,
{ {
if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange)) if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
{ {
return gl::Error(GL_NO_ERROR); return NoError();
} }
Error error = mBuffer->getIndexRange(type, offset, count, primitiveRestartEnabled, outRange); ANGLE_TRY(mBuffer->getIndexRange(type, offset, count, primitiveRestartEnabled, outRange));
if (error.isError())
{
return error;
}
mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange); mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);
return Error(GL_NO_ERROR); return NoError();
} }
} // namespace gl } // namespace gl
...@@ -34,8 +34,8 @@ class Buffer final : public RefCountObject, public LabeledObject ...@@ -34,8 +34,8 @@ class Buffer final : public RefCountObject, public LabeledObject
void setLabel(const std::string &label) override; void setLabel(const std::string &label) override;
const std::string &getLabel() const override; const std::string &getLabel() const override;
Error bufferData(const void *data, GLsizeiptr size, GLenum usage); Error bufferData(GLenum target, const void *data, GLsizeiptr size, GLenum usage);
Error bufferSubData(const void *data, GLsizeiptr size, GLintptr offset); Error bufferSubData(GLenum target, const void *data, GLsizeiptr size, GLintptr offset);
Error copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size); Error copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size);
Error map(GLenum access); Error map(GLenum access);
Error mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access); Error mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access);
......
...@@ -3430,4 +3430,23 @@ void Context::popDebugGroup() ...@@ -3430,4 +3430,23 @@ void Context::popDebugGroup()
mGLState.getDebug().popGroup(); mGLState.getDebug().popGroup();
} }
void Context::bufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
{
Buffer *buffer = mGLState.getTargetBuffer(target);
ASSERT(buffer);
handleError(buffer->bufferData(target, data, size, usage));
}
void Context::bufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)
{
if (data == nullptr)
{
return;
}
Buffer *buffer = mGLState.getTargetBuffer(target);
ASSERT(buffer);
handleError(buffer->bufferSubData(target, data, size, offset));
}
} // namespace gl } // namespace gl
...@@ -567,6 +567,9 @@ class Context final : public ValidationContext ...@@ -567,6 +567,9 @@ class Context final : public ValidationContext
GLint components, GLint components,
const GLfloat *coeffs); const GLfloat *coeffs);
void bufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
void bufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
void handleError(const Error &error) override; void handleError(const Error &error) override;
GLenum getError(); GLenum getError();
......
...@@ -23,8 +23,8 @@ class BufferImpl : angle::NonCopyable ...@@ -23,8 +23,8 @@ class BufferImpl : angle::NonCopyable
public: public:
virtual ~BufferImpl() { } virtual ~BufferImpl() { }
virtual gl::Error setData(const void *data, size_t size, GLenum usage) = 0; virtual gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) = 0;
virtual gl::Error setSubData(const void *data, size_t size, size_t offset) = 0; virtual gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) = 0;
virtual gl::Error copySubData(BufferImpl *source, virtual gl::Error copySubData(BufferImpl *source,
GLintptr sourceOffset, GLintptr sourceOffset,
GLintptr destOffset, GLintptr destOffset,
......
...@@ -21,8 +21,8 @@ class MockBufferImpl : public BufferImpl ...@@ -21,8 +21,8 @@ class MockBufferImpl : public BufferImpl
public: public:
~MockBufferImpl() { destructor(); } ~MockBufferImpl() { destructor(); }
MOCK_METHOD3(setData, gl::Error(const void*, size_t, GLenum)); MOCK_METHOD4(setData, gl::Error(GLenum, const void *, size_t, GLenum));
MOCK_METHOD3(setSubData, gl::Error(const void*, size_t, size_t)); MOCK_METHOD4(setSubData, gl::Error(GLenum, const void *, size_t, size_t));
MOCK_METHOD4(copySubData, gl::Error(BufferImpl *, GLintptr, GLintptr, GLsizeiptr)); MOCK_METHOD4(copySubData, gl::Error(BufferImpl *, GLintptr, GLintptr, GLsizeiptr));
MOCK_METHOD2(map, gl::Error(GLenum, GLvoid **)); MOCK_METHOD2(map, gl::Error(GLenum, GLvoid **));
MOCK_METHOD4(mapRange, gl::Error(size_t, size_t, GLbitfield, GLvoid **)); MOCK_METHOD4(mapRange, gl::Error(size_t, size_t, GLbitfield, GLvoid **));
......
...@@ -43,7 +43,7 @@ enum class CopyResult ...@@ -43,7 +43,7 @@ enum class CopyResult
namespace gl_d3d11 namespace gl_d3d11
{ {
D3D11_MAP GetD3DMapTypeFromBits(GLbitfield access) D3D11_MAP GetD3DMapTypeFromBits(BufferUsage usage, GLbitfield access)
{ {
bool readBit = ((access & GL_MAP_READ_BIT) != 0); bool readBit = ((access & GL_MAP_READ_BIT) != 0);
bool writeBit = ((access & GL_MAP_WRITE_BIT) != 0); bool writeBit = ((access & GL_MAP_WRITE_BIT) != 0);
...@@ -59,7 +59,8 @@ D3D11_MAP GetD3DMapTypeFromBits(GLbitfield access) ...@@ -59,7 +59,8 @@ D3D11_MAP GetD3DMapTypeFromBits(GLbitfield access)
} }
else if (writeBit && !readBit) else if (writeBit && !readBit)
{ {
return D3D11_MAP_WRITE; // Special case for uniform storage - we only allow full buffer updates.
return usage == BUFFER_USAGE_UNIFORM ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE;
} }
else if (writeBit && readBit) else if (writeBit && readBit)
{ {
...@@ -88,7 +89,7 @@ class Buffer11::BufferStorage : angle::NonCopyable ...@@ -88,7 +89,7 @@ class Buffer11::BufferStorage : angle::NonCopyable
size_t getSize() const { return mBufferSize; } size_t getSize() const { return mBufferSize; }
void setDataRevision(DataRevision rev) { mRevision = rev; } void setDataRevision(DataRevision rev) { mRevision = rev; }
virtual bool isMappable() const = 0; virtual bool isMappable(GLbitfield access) const = 0;
virtual gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source, virtual gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source,
size_t sourceOffset, size_t sourceOffset,
...@@ -123,7 +124,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage ...@@ -123,7 +124,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
const angle::BroadcastChannel *onStorageChanged); const angle::BroadcastChannel *onStorageChanged);
~NativeStorage() override; ~NativeStorage() override;
bool isMappable() const override { return mUsage == BUFFER_USAGE_STAGING; } bool isMappable(GLbitfield access) const override;
ID3D11Buffer *getNativeStorage() const { return mNativeStorage; } ID3D11Buffer *getNativeStorage() const { return mNativeStorage; }
gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source, gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source,
...@@ -157,7 +158,7 @@ class Buffer11::EmulatedIndexedStorage : public Buffer11::BufferStorage ...@@ -157,7 +158,7 @@ class Buffer11::EmulatedIndexedStorage : public Buffer11::BufferStorage
EmulatedIndexedStorage(Renderer11 *renderer); EmulatedIndexedStorage(Renderer11 *renderer);
~EmulatedIndexedStorage() override; ~EmulatedIndexedStorage() override;
bool isMappable() const override { return true; } bool isMappable(GLbitfield access) const override { return true; }
gl::ErrorOrResult<ID3D11Buffer *> getNativeStorage(SourceIndexData *indexInfo, gl::ErrorOrResult<ID3D11Buffer *> getNativeStorage(SourceIndexData *indexInfo,
const TranslatedAttribute &attribute, const TranslatedAttribute &attribute,
...@@ -190,7 +191,7 @@ class Buffer11::PackStorage : public Buffer11::BufferStorage ...@@ -190,7 +191,7 @@ class Buffer11::PackStorage : public Buffer11::BufferStorage
explicit PackStorage(Renderer11 *renderer); explicit PackStorage(Renderer11 *renderer);
~PackStorage() override; ~PackStorage() override;
bool isMappable() const override { return true; } bool isMappable(GLbitfield access) const override { return true; }
gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source, gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source,
size_t sourceOffset, size_t sourceOffset,
size_t size, size_t size,
...@@ -225,7 +226,7 @@ class Buffer11::SystemMemoryStorage : public Buffer11::BufferStorage ...@@ -225,7 +226,7 @@ class Buffer11::SystemMemoryStorage : public Buffer11::BufferStorage
explicit SystemMemoryStorage(Renderer11 *renderer); explicit SystemMemoryStorage(Renderer11 *renderer);
~SystemMemoryStorage() override {} ~SystemMemoryStorage() override {}
bool isMappable() const override { return true; } bool isMappable(GLbitfield access) const override { return true; }
gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source, gl::ErrorOrResult<CopyResult> copyFromStorage(BufferStorage *source,
size_t sourceOffset, size_t sourceOffset,
size_t size, size_t size,
...@@ -272,10 +273,10 @@ Buffer11::~Buffer11() ...@@ -272,10 +273,10 @@ Buffer11::~Buffer11()
mRenderer->onBufferDelete(this); mRenderer->onBufferDelete(this);
} }
gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage) gl::Error Buffer11::setData(GLenum target, const void *data, size_t size, GLenum usage)
{ {
updateD3DBufferUsage(usage); updateD3DBufferUsage(usage);
ANGLE_TRY(setSubData(data, size, 0)); ANGLE_TRY(setSubData(target, data, size, 0));
return gl::NoError(); return gl::NoError();
} }
...@@ -299,16 +300,20 @@ gl::ErrorOrResult<Buffer11::SystemMemoryStorage *> Buffer11::getSystemMemoryStor ...@@ -299,16 +300,20 @@ gl::ErrorOrResult<Buffer11::SystemMemoryStorage *> Buffer11::getSystemMemoryStor
return GetAs<SystemMemoryStorage>(storage); return GetAs<SystemMemoryStorage>(storage);
} }
gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset) gl::Error Buffer11::setSubData(GLenum target, const void *data, size_t size, size_t offset)
{ {
size_t requiredSize = size + offset; size_t requiredSize = size + offset;
if (data && size > 0) if (data && size > 0)
{ {
// Use system memory storage for dynamic buffers. // Use system memory storage for dynamic buffers.
// Try using a constant storage for constant buffers
BufferStorage *writeBuffer = nullptr; BufferStorage *writeBuffer = nullptr;
if (supportsDirectBinding()) if (target == GL_UNIFORM_BUFFER && offset == 0 && size >= mSize)
{
ANGLE_TRY_RESULT(getBufferStorage(BUFFER_USAGE_UNIFORM), writeBuffer);
}
else if (supportsDirectBinding())
{ {
ANGLE_TRY_RESULT(getStagingStorage(), writeBuffer); ANGLE_TRY_RESULT(getStagingStorage(), writeBuffer);
} }
...@@ -363,11 +368,12 @@ gl::Error Buffer11::copySubData(BufferImpl *source, ...@@ -363,11 +368,12 @@ gl::Error Buffer11::copySubData(BufferImpl *source,
// If copying to/from a pixel pack buffer, we must have a staging or // If copying to/from a pixel pack buffer, we must have a staging or
// pack buffer partner, because other native buffers can't be mapped // pack buffer partner, because other native buffers can't be mapped
if (copyDest->getUsage() == BUFFER_USAGE_PIXEL_PACK && !copySource->isMappable()) if (copyDest->getUsage() == BUFFER_USAGE_PIXEL_PACK && !copySource->isMappable(GL_MAP_READ_BIT))
{ {
ANGLE_TRY_RESULT(sourceBuffer->getStagingStorage(), copySource); ANGLE_TRY_RESULT(sourceBuffer->getStagingStorage(), copySource);
} }
else if (copySource->getUsage() == BUFFER_USAGE_PIXEL_PACK && !copyDest->isMappable()) else if (copySource->getUsage() == BUFFER_USAGE_PIXEL_PACK &&
!copyDest->isMappable(GL_MAP_WRITE_BIT))
{ {
ANGLE_TRY_RESULT(getStagingStorage(), copyDest); ANGLE_TRY_RESULT(getStagingStorage(), copyDest);
} }
...@@ -746,7 +752,7 @@ gl::Error Buffer11::updateBufferStorage(BufferStorage *storage, ...@@ -746,7 +752,7 @@ gl::Error Buffer11::updateBufferStorage(BufferStorage *storage,
// data directly. If we're already using a staging buffer we're fine. // data directly. If we're already using a staging buffer we're fine.
if (latestBuffer->getUsage() != BUFFER_USAGE_STAGING && if (latestBuffer->getUsage() != BUFFER_USAGE_STAGING &&
storage->getUsage() != BUFFER_USAGE_STAGING && storage->getUsage() != BUFFER_USAGE_STAGING &&
(!latestBuffer->isMappable() || !storage->isMappable())) (!latestBuffer->isMappable(GL_MAP_READ_BIT) || !storage->isMappable(GL_MAP_WRITE_BIT)))
{ {
NativeStorage *stagingBuffer = nullptr; NativeStorage *stagingBuffer = nullptr;
ANGLE_TRY_RESULT(getStagingStorage(), stagingBuffer); ANGLE_TRY_RESULT(getStagingStorage(), stagingBuffer);
...@@ -852,7 +858,7 @@ Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage) ...@@ -852,7 +858,7 @@ Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage)
gl::Error Buffer11::BufferStorage::setData(const uint8_t *data, size_t offset, size_t size) gl::Error Buffer11::BufferStorage::setData(const uint8_t *data, size_t offset, size_t size)
{ {
ASSERT(isMappable()); ASSERT(isMappable(GL_MAP_WRITE_BIT));
uint8_t *writePointer = nullptr; uint8_t *writePointer = nullptr;
ANGLE_TRY(map(offset, size, GL_MAP_WRITE_BIT, &writePointer)); ANGLE_TRY(map(offset, size, GL_MAP_WRITE_BIT, &writePointer));
...@@ -876,6 +882,17 @@ Buffer11::NativeStorage::~NativeStorage() ...@@ -876,6 +882,17 @@ Buffer11::NativeStorage::~NativeStorage()
SafeRelease(mNativeStorage); SafeRelease(mNativeStorage);
} }
bool Buffer11::NativeStorage::isMappable(GLbitfield access) const
{
if ((access & GL_MAP_READ_BIT) != 0)
{
// Read is more exclusive than write mappability.
return (mUsage == BUFFER_USAGE_STAGING);
}
ASSERT((access & GL_MAP_WRITE_BIT) != 0);
return (mUsage == BUFFER_USAGE_STAGING || mUsage == BUFFER_USAGE_UNIFORM);
}
// Returns true if it recreates the direct buffer // Returns true if it recreates the direct buffer
gl::ErrorOrResult<CopyResult> Buffer11::NativeStorage::copyFromStorage(BufferStorage *source, gl::ErrorOrResult<CopyResult> Buffer11::NativeStorage::copyFromStorage(BufferStorage *source,
size_t sourceOffset, size_t sourceOffset,
...@@ -897,7 +914,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::NativeStorage::copyFromStorage(BufferSto ...@@ -897,7 +914,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::NativeStorage::copyFromStorage(BufferSto
if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK || if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY) source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY)
{ {
ASSERT(source->isMappable()); ASSERT(source->isMappable(GL_MAP_READ_BIT));
uint8_t *sourcePointer = nullptr; uint8_t *sourcePointer = nullptr;
ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourcePointer)); ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourcePointer));
...@@ -1055,11 +1072,11 @@ gl::Error Buffer11::NativeStorage::map(size_t offset, ...@@ -1055,11 +1072,11 @@ gl::Error Buffer11::NativeStorage::map(size_t offset,
GLbitfield access, GLbitfield access,
uint8_t **mapPointerOut) uint8_t **mapPointerOut)
{ {
ASSERT(mUsage == BUFFER_USAGE_STAGING); ASSERT(isMappable(access));
D3D11_MAPPED_SUBRESOURCE mappedResource; D3D11_MAPPED_SUBRESOURCE mappedResource;
ID3D11DeviceContext *context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(access); D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(mUsage, access);
UINT d3dMapFlag = ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0 ? D3D11_MAP_FLAG_DO_NOT_WAIT : 0); UINT d3dMapFlag = ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0 ? D3D11_MAP_FLAG_DO_NOT_WAIT : 0);
HRESULT result = context->Map(mNativeStorage, 0, d3dMapType, d3dMapFlag, &mappedResource); HRESULT result = context->Map(mNativeStorage, 0, d3dMapType, d3dMapFlag, &mappedResource);
...@@ -1076,7 +1093,7 @@ gl::Error Buffer11::NativeStorage::map(size_t offset, ...@@ -1076,7 +1093,7 @@ gl::Error Buffer11::NativeStorage::map(size_t offset,
void Buffer11::NativeStorage::unmap() void Buffer11::NativeStorage::unmap()
{ {
ASSERT(mUsage == BUFFER_USAGE_STAGING); ASSERT(isMappable(GL_MAP_WRITE_BIT) || isMappable(GL_MAP_READ_BIT));
ID3D11DeviceContext *context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->Unmap(mNativeStorage, 0); context->Unmap(mNativeStorage, 0);
} }
...@@ -1215,7 +1232,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::EmulatedIndexedStorage::copyFromStorage( ...@@ -1215,7 +1232,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::EmulatedIndexedStorage::copyFromStorage(
size_t size, size_t size,
size_t destOffset) size_t destOffset)
{ {
ASSERT(source->isMappable()); ASSERT(source->isMappable(GL_MAP_READ_BIT));
uint8_t *sourceData = nullptr; uint8_t *sourceData = nullptr;
ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData));
ASSERT(destOffset + size <= mMemoryBuffer.size()); ASSERT(destOffset + size <= mMemoryBuffer.size());
...@@ -1271,7 +1288,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::PackStorage::copyFromStorage(BufferStora ...@@ -1271,7 +1288,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::PackStorage::copyFromStorage(BufferStora
// We copy through a staging buffer when drawing with a pack buffer, or for other cases where we // We copy through a staging buffer when drawing with a pack buffer, or for other cases where we
// access the pack buffer // access the pack buffer
ASSERT(source->isMappable() && source->getUsage() == BUFFER_USAGE_STAGING); ASSERT(source->isMappable(GL_MAP_READ_BIT) && source->getUsage() == BUFFER_USAGE_STAGING);
uint8_t *sourceData = nullptr; uint8_t *sourceData = nullptr;
ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData));
ASSERT(destOffset + size <= mMemoryBuffer.size()); ASSERT(destOffset + size <= mMemoryBuffer.size());
...@@ -1394,7 +1411,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::SystemMemoryStorage::copyFromStorage(Buf ...@@ -1394,7 +1411,7 @@ gl::ErrorOrResult<CopyResult> Buffer11::SystemMemoryStorage::copyFromStorage(Buf
size_t size, size_t size,
size_t destOffset) size_t destOffset)
{ {
ASSERT(source->isMappable()); ASSERT(source->isMappable(GL_MAP_READ_BIT));
uint8_t *sourceData = nullptr; uint8_t *sourceData = nullptr;
ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); ANGLE_TRY(source->map(sourceOffset, size, GL_MAP_READ_BIT, &sourceData));
ASSERT(destOffset + size <= mSystemCopy.size()); ASSERT(destOffset + size <= mSystemCopy.size());
......
...@@ -68,8 +68,8 @@ class Buffer11 : public BufferD3D ...@@ -68,8 +68,8 @@ class Buffer11 : public BufferD3D
void invalidateStaticData() override; void invalidateStaticData() override;
// BufferImpl implementation // BufferImpl implementation
gl::Error setData(const void *data, size_t size, GLenum usage) override; gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) override;
gl::Error setSubData(const void *data, size_t size, size_t offset) override; gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) override;
gl::Error copySubData(BufferImpl *source, gl::Error copySubData(BufferImpl *source,
GLintptr sourceOffset, GLintptr sourceOffset,
GLintptr destOffset, GLintptr destOffset,
......
...@@ -22,7 +22,7 @@ Buffer9::~Buffer9() ...@@ -22,7 +22,7 @@ Buffer9::~Buffer9()
mSize = 0; mSize = 0;
} }
gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage) gl::Error Buffer9::setData(GLenum /*target*/, const void *data, size_t size, GLenum usage)
{ {
if (size > mMemory.size()) if (size > mMemory.size())
{ {
...@@ -51,7 +51,7 @@ gl::Error Buffer9::getData(const uint8_t **outData) ...@@ -51,7 +51,7 @@ gl::Error Buffer9::getData(const uint8_t **outData)
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error Buffer9::setSubData(const void* data, size_t size, size_t offset) gl::Error Buffer9::setSubData(GLenum /*target*/, const void *data, size_t size, size_t offset)
{ {
if (offset + size > mMemory.size()) if (offset + size > mMemory.size())
{ {
......
...@@ -29,13 +29,16 @@ class Buffer9 : public BufferD3D ...@@ -29,13 +29,16 @@ class Buffer9 : public BufferD3D
gl::Error getData(const uint8_t **outData) override; gl::Error getData(const uint8_t **outData) override;
// BufferImpl implementation // BufferImpl implementation
virtual gl::Error setData(const void* data, size_t size, GLenum usage); gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) override;
virtual gl::Error setSubData(const void* data, size_t size, size_t offset); gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) override;
virtual gl::Error copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size); gl::Error copySubData(BufferImpl *source,
virtual gl::Error map(GLenum access, GLvoid **mapPtr); GLintptr sourceOffset,
virtual gl::Error mapRange(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr); GLintptr destOffset,
virtual gl::Error unmap(GLboolean *result); GLsizeiptr size) override;
virtual gl::Error markTransformFeedbackUsage(); 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;
private: private:
MemoryBuffer mMemory; MemoryBuffer mMemory;
......
...@@ -53,7 +53,7 @@ BufferGL::~BufferGL() ...@@ -53,7 +53,7 @@ BufferGL::~BufferGL()
mBufferID = 0; mBufferID = 0;
} }
gl::Error BufferGL::setData(const void* data, size_t size, GLenum usage) gl::Error BufferGL::setData(GLenum /*target*/, const void *data, size_t size, GLenum usage)
{ {
mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID); mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
mFunctions->bufferData(DestBufferOperationTarget, size, data, usage); mFunctions->bufferData(DestBufferOperationTarget, size, data, usage);
...@@ -76,7 +76,7 @@ gl::Error BufferGL::setData(const void* data, size_t size, GLenum usage) ...@@ -76,7 +76,7 @@ gl::Error BufferGL::setData(const void* data, size_t size, GLenum usage)
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error BufferGL::setSubData(const void* data, size_t size, size_t offset) gl::Error BufferGL::setSubData(GLenum /*target*/, const void *data, size_t size, size_t offset)
{ {
mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID); mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
mFunctions->bufferSubData(DestBufferOperationTarget, offset, size, data); mFunctions->bufferSubData(DestBufferOperationTarget, offset, size, data);
......
...@@ -24,8 +24,8 @@ class BufferGL : public BufferImpl ...@@ -24,8 +24,8 @@ class BufferGL : public BufferImpl
BufferGL(const FunctionsGL *functions, StateManagerGL *stateManager); BufferGL(const FunctionsGL *functions, StateManagerGL *stateManager);
~BufferGL() override; ~BufferGL() override;
gl::Error setData(const void* data, size_t size, GLenum usage) override; gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) override;
gl::Error setSubData(const void* data, size_t size, size_t offset) override; gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) override;
gl::Error copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size) override; gl::Error copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size) override;
gl::Error map(GLenum access, GLvoid **mapPtr) 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 mapRange(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr) override;
......
...@@ -22,13 +22,13 @@ BufferVk::~BufferVk() ...@@ -22,13 +22,13 @@ BufferVk::~BufferVk()
{ {
} }
gl::Error BufferVk::setData(const void *data, size_t size, GLenum usage) gl::Error BufferVk::setData(GLenum target, const void *data, size_t size, GLenum usage)
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
} }
gl::Error BufferVk::setSubData(const void *data, size_t size, size_t offset) gl::Error BufferVk::setSubData(GLenum target, const void *data, size_t size, size_t offset)
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
......
...@@ -21,8 +21,8 @@ class BufferVk : public BufferImpl ...@@ -21,8 +21,8 @@ class BufferVk : public BufferImpl
BufferVk(); BufferVk();
~BufferVk() override; ~BufferVk() override;
gl::Error setData(const void *data, size_t size, GLenum usage) override; gl::Error setData(GLenum target, const void *data, size_t size, GLenum usage) override;
gl::Error setSubData(const void *data, size_t size, size_t offset) override; gl::Error setSubData(GLenum target, const void *data, size_t size, size_t offset) override;
gl::Error copySubData(BufferImpl *source, gl::Error copySubData(BufferImpl *source,
GLintptr sourceOffset, GLintptr sourceOffset,
GLintptr destOffset, GLintptr destOffset,
......
...@@ -233,7 +233,7 @@ bool ValidFramebufferTarget(GLenum target) ...@@ -233,7 +233,7 @@ bool ValidFramebufferTarget(GLenum target)
} }
} }
bool ValidBufferTarget(const Context *context, GLenum target) bool ValidBufferTarget(const ValidationContext *context, GLenum target)
{ {
switch (target) switch (target)
{ {
......
...@@ -37,7 +37,7 @@ bool ValidTextureExternalTarget(const ValidationContext *context, GLenum target) ...@@ -37,7 +37,7 @@ bool ValidTextureExternalTarget(const ValidationContext *context, GLenum target)
bool ValidTexture2DDestinationTarget(const ValidationContext *context, GLenum target); bool ValidTexture2DDestinationTarget(const ValidationContext *context, GLenum target);
bool ValidTexture3DDestinationTarget(const ValidationContext *context, GLenum target); bool ValidTexture3DDestinationTarget(const ValidationContext *context, GLenum target);
bool ValidFramebufferTarget(GLenum target); bool ValidFramebufferTarget(GLenum target);
bool ValidBufferTarget(const Context *context, GLenum target); bool ValidBufferTarget(const ValidationContext *context, GLenum target);
bool ValidBufferParameter(const Context *context, GLenum pname); bool ValidBufferParameter(const Context *context, GLenum pname);
bool ValidMipLevel(const ValidationContext *context, GLenum target, GLint level); bool ValidMipLevel(const ValidationContext *context, GLenum target, GLint level);
bool ValidImageSizeParameters(const Context *context, bool ValidImageSizeParameters(const Context *context,
......
...@@ -3325,6 +3325,111 @@ bool ValidateCreateShader(Context *context, GLenum type) ...@@ -3325,6 +3325,111 @@ bool ValidateCreateShader(Context *context, GLenum type)
context->handleError(Error(GL_INVALID_ENUM)); context->handleError(Error(GL_INVALID_ENUM));
return false; return false;
} }
return true;
}
bool ValidateBufferData(ValidationContext *context,
GLenum target,
GLsizeiptr size,
const GLvoid *data,
GLenum usage)
{
if (size < 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
switch (usage)
{
case GL_STREAM_DRAW:
case GL_STATIC_DRAW:
case GL_DYNAMIC_DRAW:
break;
case GL_STREAM_READ:
case GL_STREAM_COPY:
case GL_STATIC_READ:
case GL_STATIC_COPY:
case GL_DYNAMIC_READ:
case GL_DYNAMIC_COPY:
if (context->getClientMajorVersion() < 3)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
return true;
}
bool ValidateBufferSubData(ValidationContext *context,
GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid *data)
{
if (size < 0 || offset < 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (buffer->isMapped())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// Check for possible overflow of size + offset
angle::CheckedNumeric<size_t> checkedSize(size);
checkedSize += offset;
if (!checkedSize.IsValid())
{
context->handleError(Error(GL_OUT_OF_MEMORY));
return false;
}
if (size + offset > buffer->getSize())
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
return true; return true;
} }
......
...@@ -306,6 +306,16 @@ bool ValidateCopySubTextureCHROMIUM(Context *context, ...@@ -306,6 +306,16 @@ bool ValidateCopySubTextureCHROMIUM(Context *context,
GLboolean unpackUnmultiplyAlpha); GLboolean unpackUnmultiplyAlpha);
bool ValidateCreateShader(Context *context, GLenum type); bool ValidateCreateShader(Context *context, GLenum type);
bool ValidateBufferData(ValidationContext *context,
GLenum target,
GLsizeiptr size,
const GLvoid *data,
GLenum usage);
bool ValidateBufferSubData(ValidationContext *context,
GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid *data);
} // namespace gl } // namespace gl
......
...@@ -427,57 +427,12 @@ void GL_APIENTRY BufferData(GLenum target, GLsizeiptr size, const GLvoid* data, ...@@ -427,57 +427,12 @@ void GL_APIENTRY BufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
Context *context = GetValidGlobalContext(); Context *context = GetValidGlobalContext();
if (context) if (context)
{ {
if (size < 0) if (!context->skipValidation() && !ValidateBufferData(context, target, size, data, usage))
{ {
context->handleError(Error(GL_INVALID_VALUE));
return; return;
} }
switch (usage) context->bufferData(target, size, data, usage);
{
case GL_STREAM_DRAW:
case GL_STATIC_DRAW:
case GL_DYNAMIC_DRAW:
break;
case GL_STREAM_READ:
case GL_STREAM_COPY:
case GL_STATIC_READ:
case GL_STATIC_COPY:
case GL_DYNAMIC_READ:
case GL_DYNAMIC_COPY:
if (context->getClientMajorVersion() < 3)
{
context->handleError(Error(GL_INVALID_ENUM));
return;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
Error error = buffer->bufferData(data, size, usage);
if (error.isError())
{
context->handleError(error);
return;
}
} }
} }
...@@ -489,58 +444,13 @@ void GL_APIENTRY BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, ...@@ -489,58 +444,13 @@ void GL_APIENTRY BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
Context *context = GetValidGlobalContext(); Context *context = GetValidGlobalContext();
if (context) if (context)
{ {
if (size < 0 || offset < 0) if (!context->skipValidation() &&
{ !ValidateBufferSubData(context, target, offset, size, data))
context->handleError(Error(GL_INVALID_VALUE));
return;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
if (buffer->isMapped())
{
context->handleError(Error(GL_INVALID_OPERATION));
return;
}
// Check for possible overflow of size + offset
angle::CheckedNumeric<size_t> checkedSize(size);
checkedSize += offset;
if (!checkedSize.IsValid())
{
context->handleError(Error(GL_OUT_OF_MEMORY));
return;
}
if (size + offset > buffer->getSize())
{
context->handleError(Error(GL_INVALID_VALUE));
return;
}
if (data == NULL)
{ {
return; return;
} }
Error error = buffer->bufferSubData(data, size, offset); context->bufferSubData(target, offset, size, data);
if (error.isError())
{
context->handleError(error);
return;
}
} }
} }
......
...@@ -37,7 +37,8 @@ class D3D11EmulatedIndexedBufferTest : public ANGLETest ...@@ -37,7 +37,8 @@ class D3D11EmulatedIndexedBufferTest : public ANGLETest
mSourceBuffer = new rx::Buffer11(mRenderer); mSourceBuffer = new rx::Buffer11(mRenderer);
GLfloat testData[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; GLfloat testData[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f };
gl::Error error = mSourceBuffer->setData(testData, sizeof(testData), GL_STATIC_DRAW); gl::Error error =
mSourceBuffer->setData(GL_ARRAY_BUFFER, testData, sizeof(testData), GL_STATIC_DRAW);
ASSERT_FALSE(error.isError()); ASSERT_FALSE(error.isError());
mTranslatedAttribute.baseOffset = 0; mTranslatedAttribute.baseOffset = 0;
......
...@@ -77,13 +77,6 @@ class UniformBufferTest : public ANGLETest ...@@ -77,13 +77,6 @@ class UniformBufferTest : public ANGLETest
// Basic UBO functionality. // Basic UBO functionality.
TEST_P(UniformBufferTest, Simple) TEST_P(UniformBufferTest, Simple)
{ {
// TODO(jmadill): Figure out why this fails on Intel.
if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
std::cout << "Test skipped on Intel." << std::endl;
return;
}
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f}; float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f};
...@@ -104,13 +97,6 @@ TEST_P(UniformBufferTest, Simple) ...@@ -104,13 +97,6 @@ TEST_P(UniformBufferTest, Simple)
// The second step renders a color from a UBO with a non-zero offset. // The second step renders a color from a UBO with a non-zero offset.
TEST_P(UniformBufferTest, UniformBufferRange) TEST_P(UniformBufferTest, UniformBufferRange)
{ {
// TODO(jmadill): Figure out why this fails on Intel.
if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
std::cout << "Test skipped on Intel." << std::endl;
return;
}
int px = getWindowWidth() / 2; int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2; int py = getWindowHeight() / 2;
...@@ -182,13 +168,6 @@ TEST_P(UniformBufferTest, UniformBufferRange) ...@@ -182,13 +168,6 @@ TEST_P(UniformBufferTest, UniformBufferRange)
// Test uniform block bindings. // Test uniform block bindings.
TEST_P(UniformBufferTest, UniformBufferBindings) TEST_P(UniformBufferTest, UniformBufferBindings)
{ {
// TODO(jmadill): Figure out why this fails on Intel.
if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
std::cout << "Test skipped on Intel." << std::endl;
return;
}
int px = getWindowWidth() / 2; int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2; int py = getWindowHeight() / 2;
...@@ -245,11 +224,10 @@ TEST_P(UniformBufferTest, UnboundUniformBuffer) ...@@ -245,11 +224,10 @@ TEST_P(UniformBufferTest, UnboundUniformBuffer)
// https://code.google.com/p/angleproject/issues/detail?id=965 // https://code.google.com/p/angleproject/issues/detail?id=965
TEST_P(UniformBufferTest, UniformBufferManyUpdates) TEST_P(UniformBufferTest, UniformBufferManyUpdates)
{ {
// TODO(jmadill): Figure out why this fails on Intel. // TODO(jmadill): Figure out why this fails on Intel OpenGL.
if (IsIntel() && (getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE || if (IsIntel() && IsOpenGL())
getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE))
{ {
std::cout << "Test skipped on Intel." << std::endl; std::cout << "Test skipped on Intel OpenGL." << std::endl;
return; return;
} }
...@@ -286,13 +264,6 @@ TEST_P(UniformBufferTest, UniformBufferManyUpdates) ...@@ -286,13 +264,6 @@ TEST_P(UniformBufferTest, UniformBufferManyUpdates)
// Use a large number of buffer ranges (compared to the actual size of the UBO) // Use a large number of buffer ranges (compared to the actual size of the UBO)
TEST_P(UniformBufferTest, ManyUniformBufferRange) TEST_P(UniformBufferTest, ManyUniformBufferRange)
{ {
// TODO(jmadill): Figure out why this fails on Intel.
if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
{
std::cout << "Test skipped on Intel." << std::endl;
return;
}
int px = getWindowWidth() / 2; int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2; int py = getWindowHeight() / 2;
......
...@@ -82,7 +82,7 @@ class MockBufferD3D : public rx::BufferD3D ...@@ -82,7 +82,7 @@ class MockBufferD3D : public rx::BufferD3D
} }
// BufferImpl // BufferImpl
gl::Error setData(const void *data, size_t size, GLenum) override gl::Error setData(GLenum target, const void *data, size_t size, GLenum) override
{ {
mData.resize(size); mData.resize(size);
if (data && size > 0) if (data && size > 0)
...@@ -92,7 +92,7 @@ class MockBufferD3D : public rx::BufferD3D ...@@ -92,7 +92,7 @@ class MockBufferD3D : public rx::BufferD3D
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
MOCK_METHOD3(setSubData, gl::Error(const void*, size_t, size_t)); MOCK_METHOD4(setSubData, gl::Error(GLenum, const void *, size_t, size_t));
MOCK_METHOD4(copySubData, gl::Error(BufferImpl*, GLintptr, GLintptr, GLsizeiptr)); MOCK_METHOD4(copySubData, gl::Error(BufferImpl*, GLintptr, GLintptr, GLsizeiptr));
MOCK_METHOD2(map, gl::Error(GLenum, GLvoid **)); MOCK_METHOD2(map, gl::Error(GLenum, GLvoid **));
MOCK_METHOD4(mapRange, gl::Error(size_t, size_t, GLbitfield, GLvoid **)); MOCK_METHOD4(mapRange, gl::Error(size_t, size_t, GLbitfield, GLvoid **));
...@@ -152,7 +152,8 @@ IndexDataManagerPerfTest::IndexDataManagerPerfTest() ...@@ -152,7 +152,8 @@ IndexDataManagerPerfTest::IndexDataManagerPerfTest()
{ {
indexData[index] = static_cast<GLushort>(index); indexData[index] = static_cast<GLushort>(index);
} }
mIndexBuffer.bufferData(&indexData[0], indexData.size() * sizeof(GLushort), GL_STATIC_DRAW); mIndexBuffer.bufferData(GL_ARRAY_BUFFER, &indexData[0], indexData.size() * sizeof(GLushort),
GL_STATIC_DRAW);
} }
void IndexDataManagerPerfTest::step() void IndexDataManagerPerfTest::step()
......
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