From 6038c7170b8a138bce3ccdf5144a204e3c4d3c2d Mon Sep 17 00:00:00 2001 From: James Ruan Date: Wed, 3 Apr 2024 11:48:38 +0800 Subject: [PATCH] [ganesh] Apply buffer binding restriction WebGL has buffer binding restriction that mixing index buffer and other data is invalid. --- src/gpu/ganesh/gl/GrGLBuffer.cpp | 31 +++++++++++++++++++++++++++++++ src/gpu/ganesh/gl/GrGLBuffer.h | 13 +++++++++++++ src/gpu/ganesh/gl/GrGLCaps.cpp | 6 ++++++ src/gpu/ganesh/gl/GrGLCaps.h | 4 ++++ src/gpu/ganesh/gl/GrGLGpu.cpp | 1 + 5 files changed, 55 insertions(+) diff --git a/src/gpu/ganesh/gl/GrGLBuffer.cpp b/src/gpu/ganesh/gl/GrGLBuffer.cpp index 39080ea9b8dc..1ee30c92541b 100644 --- a/src/gpu/ganesh/gl/GrGLBuffer.cpp +++ b/src/gpu/ganesh/gl/GrGLBuffer.cpp @@ -100,6 +100,37 @@ inline static GrGLenum gr_to_gl_access_pattern(GrGpuBufferType bufferType, return usageType(bufferType, accessPattern); } +#ifdef SK_DEBUG +bool GrGLBuffer::validBindingTarget(GrGLenum target) const { + if (!this->glCaps().bufferBindingRestriction()) { + return true; + } + /* This restriction implies that a given buffer object may contain + * either indices or other data, but not both. + */ + if (GR_GL_ELEMENT_ARRAY_BUFFER == target) { + switch (this->fBindingCategory) { + case kUndefined_BindingCategory: + this->fBindingCategory = kIndexBuffer_BindingCategory; + [[fallthrough]]; + case kIndexBuffer_BindingCategory: + return true; + default: + return false; + } + } + switch (this->fBindingCategory) { + case kUndefined_BindingCategory: + this->fBindingCategory = kOtherData_BindingCategory; + [[fallthrough]]; + case kOtherData_BindingCategory: + return true; + default: + return false; + } +} +#endif // SK_DEBUG + GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType, diff --git a/src/gpu/ganesh/gl/GrGLBuffer.h b/src/gpu/ganesh/gl/GrGLBuffer.h index d6ec2cb5df31..288b15f6a1f3 100644 --- a/src/gpu/ganesh/gl/GrGLBuffer.h +++ b/src/gpu/ganesh/gl/GrGLBuffer.h @@ -31,6 +31,10 @@ class GrGLBuffer : public GrGpuBuffer { void setHasAttachedToTexture() { fHasAttachedToTexture = true; } bool hasAttachedToTexture() const { return fHasAttachedToTexture; } +#ifdef SK_DEBUG + bool validBindingTarget(GrGLenum target) const; +#endif // SK_DEBUG + protected: GrGLBuffer(GrGLGpu*, size_t size, @@ -59,6 +63,15 @@ class GrGLBuffer : public GrGpuBuffer { GrGLenum fUsage; bool fHasAttachedToTexture; +#ifdef SK_DEBUG + enum BindingCategory { + kUndefined_BindingCategory, + kIndexBuffer_BindingCategory, + kOtherData_BindingCategory, + }; + mutable BindingCategory fBindingCategory; +#endif // SK_DEBUG + using INHERITED = GrGpuBuffer; }; diff --git a/src/gpu/ganesh/gl/GrGLCaps.cpp b/src/gpu/ganesh/gl/GrGLCaps.cpp index 7bdbe4f72c47..743c7491f255 100644 --- a/src/gpu/ganesh/gl/GrGLCaps.cpp +++ b/src/gpu/ganesh/gl/GrGLCaps.cpp @@ -261,6 +261,12 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, fClientCanDisableMultisample = false; } + if (GR_IS_GR_GL(standard) || GR_IS_GR_GL_ES(standard)) { + fBufferBindingRestriction = false; + } else if (GR_IS_GR_WEBGL(standard)) { + fBufferBindingRestriction = true; + } + if (GR_IS_GR_GL(standard)) { // 3.1 has draw_instanced but not instanced_arrays, for the time being we only care about // instanced arrays, but we could make this more granular if we wanted diff --git a/src/gpu/ganesh/gl/GrGLCaps.h b/src/gpu/ganesh/gl/GrGLCaps.h index 16227714260a..2e28eed99a58 100644 --- a/src/gpu/ganesh/gl/GrGLCaps.h +++ b/src/gpu/ganesh/gl/GrGLCaps.h @@ -515,6 +515,9 @@ class GrGLCaps : public GrCaps { bool clientCanDisableMultisample() const { return fClientCanDisableMultisample; } + /* https://registry.khronos.org/webgl/specs/latest/2.0/#BUFFER_OBJECT_BINDING */ + bool bufferBindingRestriction() const { return fBufferBindingRestriction; } + GrBackendFormat getBackendFormatFromCompressionType(SkTextureCompressionType) const override; skgpu::Swizzle getWriteSwizzle(const GrBackendFormat&, GrColorType) const override; @@ -630,6 +633,7 @@ class GrGLCaps : public GrCaps { bool fSRGBWriteControl : 1; bool fSkipErrorChecks : 1; bool fClientCanDisableMultisample : 1; + bool fBufferBindingRestriction: 1; // Driver workarounds bool fDoManualMipmapping : 1; diff --git a/src/gpu/ganesh/gl/GrGLGpu.cpp b/src/gpu/ganesh/gl/GrGLGpu.cpp index eaa32f250b5d..6bbd3cdb7cb8 100644 --- a/src/gpu/ganesh/gl/GrGLGpu.cpp +++ b/src/gpu/ganesh/gl/GrGLGpu.cpp @@ -2158,6 +2158,7 @@ GrGLenum GrGLGpu::bindBuffer(GrGpuBufferType type, const GrBuffer* buffer) { } else if (static_cast(buffer)->uniqueID() != bufferState->fBoundBufferUniqueID) { const GrGLBuffer* glBuffer = static_cast(buffer); + SkASSERT(glBuffer->validBindingTarget(bufferState->fGLTarget)); GL_CALL(BindBuffer(bufferState->fGLTarget, glBuffer->bufferID())); bufferState->fBufferZeroKnownBound = false; bufferState->fBoundBufferUniqueID = glBuffer->uniqueID();