Reland "Use glInvalidateBufferData when available."

This is a reland of commit 02ee07325f

Original change's description:
> Use glInvalidateBufferData when available.
>
> Also move common onUpdateBufferData checks and asserts to base class.
>
> Bug: skia:13427
> Change-Id: Iccae5b654a6ed3eab71ac701a78434aa1116e00e
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/548482
> Commit-Queue: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Greg Daniel <egdaniel@google.com>

Bug: skia:13427
Change-Id: I4a133bca44b52924453b6e5d2e4e812e80900606
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/549618
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
Auto-Submit: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2022-06-13 11:41:59 -04:00 committed by SkCQ
parent 8ef302ac6b
commit db79acc89a
8 changed files with 154 additions and 135 deletions

View File

@ -46,7 +46,11 @@ bool GrGpuBuffer::isMapped() const { return SkToBool(fMapPtr); }
bool GrGpuBuffer::updateData(const void* src, size_t srcSizeInBytes) {
SkASSERT(!fHasWrittenToBuffer || fAccessPattern == kDynamic_GrAccessPattern);
SkASSERT(!this->isMapped());
SkASSERT(srcSizeInBytes <= fSizeInBytes);
SkASSERT(srcSizeInBytes > 0 && srcSizeInBytes <= fSizeInBytes);
SkASSERT(src);
if (this->wasDestroyed()) {
return false;
}
if (this->intendedType() == GrGpuBufferType::kXferGpuToCpu) {
return false;
}

View File

@ -150,10 +150,6 @@ void GrD3DBuffer::onUnmap() {
}
bool GrD3DBuffer::onUpdateData(const void* src, size_t size) {
SkASSERT(src);
if (size > this->size()) {
return false;
}
if (!fD3DResource) {
return false;
}

View File

@ -129,10 +129,6 @@ void GrDawnBuffer::onUnmap() {
}
bool GrDawnBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
if (this->wasDestroyed()) {
return false;
}
this->map();
if (!this->isMapped()) {
return false;

View File

@ -16,16 +16,16 @@
#define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X)
#define GL_ALLOC_CALL(call) \
[&] { \
if (this->glGpu()->glCaps().skipErrorChecks()) { \
GR_GL_CALL(this->glGpu()->glInterface(), call); \
return static_cast<GrGLenum>(GR_GL_NO_ERROR); \
} else { \
this->glGpu()->clearErrorsAndCheckForOOM(); \
GR_GL_CALL_NOERRCHECK(this->glGpu()->glInterface(), call); \
return this->glGpu()->getErrorAndCheckForOOM(); \
} \
#define GL_ALLOC_CALL(gpu, call) \
[&] { \
if (gpu->glCaps().skipErrorChecks()) { \
GR_GL_CALL(gpu->glInterface(), call); \
return static_cast<GrGLenum>(GR_GL_NO_ERROR); \
} else { \
gpu->clearErrorsAndCheckForOOM(); \
GR_GL_CALL_NOERRCHECK(gpu->glInterface(), call); \
return gpu->getErrorAndCheckForOOM(); \
} \
}()
#ifdef SK_DEBUG
@ -122,7 +122,10 @@ GrGLBuffer::GrGLBuffer(GrGLGpu* gpu,
// cases will always get an updateData() or map() call before use.
if (fBufferID && fIntendedType == GrGpuBufferType::kXferGpuToCpu) {
GrGLenum target = gpu->bindBuffer(fIntendedType, this);
GrGLenum error = GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)size, nullptr, fUsage));
GrGLenum error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
(GrGLsizeiptr)size,
nullptr,
fUsage));
if (error != GR_GL_NO_ERROR) {
GL_CALL(DeleteBuffers(1, &fBufferID));
fBufferID = 0;
@ -172,6 +175,23 @@ void GrGLBuffer::onAbandon() {
INHERITED::onAbandon();
}
static inline GrGLenum SK_WARN_UNUSED_RESULT invalidate_buffer(GrGLGpu* gpu,
GrGLenum target,
GrGLenum usage,
GrGLuint bufferID,
size_t bufferSize) {
switch (gpu->glCaps().invalidateBufferType()) {
case GrGLCaps::InvalidateBufferType::kNone:
return GR_GL_NO_ERROR;
case GrGLCaps::InvalidateBufferType::kNullData:
return GL_ALLOC_CALL(gpu, BufferData(target, bufferSize, nullptr, usage));
case GrGLCaps::InvalidateBufferType::kInvalidate:
GR_GL_CALL(gpu->glInterface(), InvalidateBufferData(bufferID));
return GR_GL_NO_ERROR;
}
SkUNREACHABLE;
}
void GrGLBuffer::onMap() {
SkASSERT(fBufferID);
SkASSERT(!this->wasDestroyed());
@ -188,15 +208,29 @@ void GrGLBuffer::onMap() {
case GrGLCaps::kMapBuffer_MapBufferType: {
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
if (!readOnly) {
// Let driver know it can discard the old data
if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
GrGLenum error =
GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
// Resize the buffer to the correct size if it is currently smaller and otherwise
// let the the driver know the data can be discarded.
if (fGLSizeInBytes != this->size()) {
GrGLenum error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
this->size(),
nullptr,
fUsage));
if (error != GR_GL_NO_ERROR) {
return;
}
fGLSizeInBytes = this->size();
} else {
GrGLenum error = invalidate_buffer(this->glGpu(),
target,
fUsage,
fBufferID,
this->size());
if (error != GR_GL_NO_ERROR) {
return;
}
}
}
SkASSERT(fGLSizeInBytes == this->size());
GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
break;
}
@ -204,7 +238,10 @@ void GrGLBuffer::onMap() {
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
// Make sure the GL buffer size agrees with fDesc before mapping.
if (fGLSizeInBytes != this->size()) {
GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
GrGLenum error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
this->size(),
nullptr,
fUsage));
if (error != GR_GL_NO_ERROR) {
return;
}
@ -226,7 +263,10 @@ void GrGLBuffer::onMap() {
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
// Make sure the GL buffer size agrees with fDesc before mapping.
if (fGLSizeInBytes != this->size()) {
GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
GrGLenum error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
this->size(),
nullptr,
fUsage));
if (error != GR_GL_NO_ERROR) {
return;
}
@ -269,53 +309,44 @@ void GrGLBuffer::onUnmap() {
bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
SkASSERT(fBufferID);
if (this->wasDestroyed()) {
return false;
}
SkASSERT(!this->isMapped());
VALIDATE();
if (srcSizeInBytes > this->size()) {
return false;
}
SkASSERT(srcSizeInBytes <= this->size());
// bindbuffer handles dirty context
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
if (this->glCaps().useBufferDataNullHint()) {
if (this->size() == srcSizeInBytes) {
GrGLenum error =
GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
if (error != GR_GL_NO_ERROR) {
return false;
}
} else {
// Before we call glBufferSubData we give the driver a hint using
// glBufferData with nullptr. This makes the old buffer contents
// inaccessible to future draws. The GPU may still be processing
// draws that reference the old contents. With this hint it can
// assign a different allocation for the new contents to avoid
// flushing the gpu past draws consuming the old contents.
// TODO I think we actually want to try calling bufferData here
GrGLenum error =
GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)this->size(), nullptr, fUsage));
if (error != GR_GL_NO_ERROR) {
return false;
}
GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
}
fGLSizeInBytes = this->size();
} else {
// Note that we're cheating on the size here. Currently no methods
// allow a partial update that preserves contents of non-updated
// portions of the buffer (map() does a glBufferData(..size, nullptr..))
GrGLenum error =
GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
bool noInvalidation = this->glGpu()->glCaps().invalidateBufferType() ==
GrGLCaps::InvalidateBufferType::kNone;
if (this->size() == srcSizeInBytes || fGLSizeInBytes == 0 || noInvalidation) {
// No need for a separate invalidation if we're overwriting everything. If we don't
// have a way to invalidate then we cheat a bit here. We use glBufferData but with the src
// data size. Currently, no methods allow a partial update that preserves contents of
// non-updated portions of the buffer (map() does a glBufferData(..size, nullptr..))
GrGLenum error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
(GrGLsizeiptr)srcSizeInBytes,
src,
fUsage));
if (error != GR_GL_NO_ERROR) {
return false;
}
fGLSizeInBytes = srcSizeInBytes;
} else {
GrGLenum error;
if (fGLSizeInBytes == this->size()) {
error = invalidate_buffer(this->glGpu(), target, fUsage, fBufferID, this->size());
} else {
error = GL_ALLOC_CALL(this->glGpu(), BufferData(target,
this->size(),
src,
fUsage));
fGLSizeInBytes = this->size();
}
if (error != GR_GL_NO_ERROR) {
return false;
}
GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr)srcSizeInBytes, src));
fGLSizeInBytes = this->size();
}
VALIDATE();
return true;
}

View File

@ -47,7 +47,6 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions,
fBindUniformLocationSupport = false;
fMipmapLevelControlSupport = false;
fMipmapLodControlSupport = false;
fUseBufferDataNullHint = false;
fDoManualMipmapping = false;
fClearToBoundaryValuesIsBroken = false;
fClearTextureSupport = false;
@ -311,9 +310,15 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
fMipmapLodControlSupport = false;
}
// Chrome's command buffer will zero out a buffer if null is passed to glBufferData to avoid
// letting an application see uninitialized memory. WebGL spec explicitly disallows null values.
fUseBufferDataNullHint = !GR_IS_GR_WEBGL(standard) && !ctxInfo.isOverCommandBuffer();
if ((GR_IS_GR_GL_ES(standard) || GR_IS_GR_GL(standard)) &&
ctxInfo.hasExtension("GL_ARB_invalidate_subdata")) {
fInvalidateBufferType = InvalidateBufferType::kInvalidate;
} else if (!GR_IS_GR_WEBGL(standard) && !ctxInfo.isOverCommandBuffer()) {
// Chrome's command buffer will push an array of zeros to a buffer if null is passed to
// glBufferData (to avoid letting an application see uninitialized memory). This is
// expensive so we avoid it. WebGL spec explicitly disallows null values.
fInvalidateBufferType = InvalidateBufferType::kNullData;
}
if (GR_IS_GR_GL(standard)) {
fClearTextureSupport = (version >= GR_GL_VER(4,4) ||
@ -1156,46 +1161,50 @@ void GrGLCaps::onDumpJSON(SkJSONWriter* writer) const {
writer->endArray();
static const char* kMSFBOExtStr[] = {
"None",
"Standard",
"Apple",
"IMG MS To Texture",
"EXT MS To Texture",
auto msfboStr = [&] {
switch (fMSFBOType) {
case kNone_MSFBOType: return "None";
case kStandard_MSFBOType: return "Standard";
case kES_Apple_MSFBOType: return "Apple";
case kES_IMG_MsToTexture_MSFBOType: return "IMG MS To Texture";
case kES_EXT_MsToTexture_MSFBOType: return "EXT MS To Texture";
}
SkUNREACHABLE;
};
static_assert(0 == kNone_MSFBOType);
static_assert(1 == kStandard_MSFBOType);
static_assert(2 == kES_Apple_MSFBOType);
static_assert(3 == kES_IMG_MsToTexture_MSFBOType);
static_assert(4 == kES_EXT_MsToTexture_MSFBOType);
static_assert(SK_ARRAY_COUNT(kMSFBOExtStr) == kLast_MSFBOType + 1);
static const char* kInvalidateFBTypeStr[] = {
"None",
"Discard",
"Invalidate",
auto invalidateFBTypeStr = [&] {
switch (fInvalidateFBType) {
case kNone_InvalidateFBType: return "None";
case kDiscard_InvalidateFBType: return "Discard";
case kInvalidate_InvalidateFBType: return "Invalidate";
}
SkUNREACHABLE;
};
static_assert(0 == kNone_InvalidateFBType);
static_assert(1 == kDiscard_InvalidateFBType);
static_assert(2 == kInvalidate_InvalidateFBType);
static_assert(SK_ARRAY_COUNT(kInvalidateFBTypeStr) == kLast_InvalidateFBType + 1);
static const char* kMapBufferTypeStr[] = {
"None",
"MapBuffer",
"MapBufferRange",
"Chromium",
auto invalidateBufferTypeStr = [&] {
switch (fInvalidateBufferType) {
case InvalidateBufferType::kNone: return "None";
case InvalidateBufferType::kNullData: return "Null data hint";
case InvalidateBufferType::kInvalidate: return "Invalidate";
}
SkUNREACHABLE;
};
auto mapBufferTypeStr = [&] {
switch (fMapBufferType) {
case kNone_MapBufferType: return "None";
case kMapBuffer_MapBufferType: return "MapBuffer";
case kMapBufferRange_MapBufferType: return "MapBufferRange";
case kChromium_MapBufferType: return "Chromium";
}
SkUNREACHABLE;
};
static_assert(0 == kNone_MapBufferType);
static_assert(1 == kMapBuffer_MapBufferType);
static_assert(2 == kMapBufferRange_MapBufferType);
static_assert(3 == kChromium_MapBufferType);
static_assert(SK_ARRAY_COUNT(kMapBufferTypeStr) == kLast_MapBufferType + 1);
writer->appendBool("Core Profile", fIsCoreProfile);
writer->appendCString("MSAA Type", kMSFBOExtStr[fMSFBOType]);
writer->appendCString("Invalidate FB Type", kInvalidateFBTypeStr[fInvalidateFBType]);
writer->appendCString("Map Buffer Type", kMapBufferTypeStr[fMapBufferType]);
writer->appendCString("MSAA Type", msfboStr());
writer->appendCString("Invalidate FB Type", invalidateFBTypeStr());
writer->appendCString("Invalidate Buffer Type", invalidateBufferTypeStr());
writer->appendCString("Map Buffer Type", mapBufferTypeStr());
writer->appendCString("Multi Draw Type", multi_draw_type_name(fMultiDrawType));
writer->appendS32("Max FS Uniform Vectors", fMaxFragmentUniformVectors);
writer->appendBool("Pack Flip Y support", fPackFlipYSupport);
@ -1211,7 +1220,6 @@ void GrGLCaps::onDumpJSON(SkJSONWriter* writer) const {
writer->appendBool("Rectangle texture support", fRectangleTextureSupport);
writer->appendBool("Mipmap LOD control support", fMipmapLodControlSupport);
writer->appendBool("Mipmap level control support", fMipmapLevelControlSupport);
writer->appendBool("Use buffer data null hint", fUseBufferDataNullHint);
writer->appendBool("Clear texture support", fClearTextureSupport);
writer->appendBool("Program binary support", fProgramBinarySupport);
writer->appendBool("Program parameters support", fProgramParameterSupport);

View File

@ -60,8 +60,6 @@ public:
* GL_MAX_SAMPLES value.
*/
kES_EXT_MsToTexture_MSFBOType,
kLast_MSFBOType = kES_EXT_MsToTexture_MSFBOType
};
enum BlitFramebufferFlags {
@ -76,19 +74,21 @@ public:
enum InvalidateFBType {
kNone_InvalidateFBType,
kDiscard_InvalidateFBType, //<! glDiscardFramebuffer()
kInvalidate_InvalidateFBType, //<! glInvalidateFramebuffer()
kDiscard_InvalidateFBType, //<! glDiscardFramebuffer()
kInvalidate_InvalidateFBType, //<! glInvalidateFramebuffer()
};
kLast_InvalidateFBType = kInvalidate_InvalidateFBType
enum class InvalidateBufferType {
kNone,
kNullData, // Call glBufferData with a null data pointer.
kInvalidate // glInvalidateBufferData
};
enum MapBufferType {
kNone_MapBufferType,
kMapBuffer_MapBufferType, // glMapBuffer()
kMapBufferRange_MapBufferType, // glMapBufferRange()
kChromium_MapBufferType, // GL_CHROMIUM_map_sub
kLast_MapBufferType = kChromium_MapBufferType,
kMapBuffer_MapBufferType, // glMapBuffer()
kMapBufferRange_MapBufferType, // glMapBufferRange()
kChromium_MapBufferType, // GL_CHROMIUM_map_sub
};
enum class TransferBufferType {
@ -361,7 +361,7 @@ public:
void onDumpJSON(SkJSONWriter*) const override;
bool useBufferDataNullHint() const { return fUseBufferDataNullHint; }
InvalidateBufferType invalidateBufferType() const { return fInvalidateBufferType; }
// Certain Intel GPUs on Mac fail to clear if the glClearColor is made up of only 1s and 0s.
bool clearToBoundaryValuesIsBroken() const { return fClearToBoundaryValuesIsBroken; }
@ -557,12 +557,13 @@ private:
int fMaxFragmentUniformVectors = 0;
float fMaxTextureMaxAnisotropy = 1.f;
MSFBOType fMSFBOType = kNone_MSFBOType;
InvalidateFBType fInvalidateFBType = kNone_InvalidateFBType;
MapBufferType fMapBufferType = kNone_MapBufferType;
TransferBufferType fTransferBufferType = TransferBufferType::kNone;
FenceType fFenceType = FenceType::kNone;
MultiDrawType fMultiDrawType = MultiDrawType::kNone;
MSFBOType fMSFBOType = kNone_MSFBOType;
InvalidateFBType fInvalidateFBType = kNone_InvalidateFBType;
InvalidateBufferType fInvalidateBufferType = InvalidateBufferType::kNone;
MapBufferType fMapBufferType = kNone_MapBufferType;
TransferBufferType fTransferBufferType = TransferBufferType::kNone;
FenceType fFenceType = FenceType::kNone;
MultiDrawType fMultiDrawType = MultiDrawType::kNone;
bool fPackFlipYSupport : 1;
bool fTextureUsageSupport : 1;
@ -578,7 +579,6 @@ private:
bool fRectangleTextureSupport : 1;
bool fMipmapLevelControlSupport : 1;
bool fMipmapLodControlSupport : 1;
bool fUseBufferDataNullHint : 1;
bool fClearTextureSupport : 1;
bool fProgramBinarySupport : 1;
bool fProgramParameterSupport : 1;

View File

@ -83,14 +83,6 @@ GrMtlBuffer::~GrMtlBuffer() {
}
bool GrMtlBuffer::onUpdateData(const void* src, size_t sizeInBytes) {
if (this->wasDestroyed()) {
return false;
}
if (sizeInBytes > this->size()) {
return false;
}
if (fIsDynamic) {
this->internalMap(sizeInBytes);
if (!fMapPtr) {

View File

@ -299,14 +299,6 @@ void GrVkBuffer::onUnmap() {
}
bool GrVkBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
if (this->wasDestroyed()) {
return false;
}
if (srcSizeInBytes > this->size()) {
return false;
}
if (this->isVkMappable()) {
this->vkMap(srcSizeInBytes);
if (!fMapPtr) {