Add GrContext::oomed() and implement for GL and VK.
Surfaces to client whether GrContext has seen a GL_OUT_MEMORY, VK_ERROR_OUT_OF_HOST_MEMORY, or VK_ERROR_OUT_OF_DEVICE_MEMORY error. Bug: chromium:1093997 Change-Id: I8e9799a0f7d8a74df056629d7d1d07c0d0a0fe30 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/298216 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
03b92cd667
commit
24069ebd23
@ -7,7 +7,9 @@ This file includes a list of high level updates for each milestone release.
|
|||||||
Milestone 85
|
Milestone 85
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* <insert new release notes here>
|
* Added GrContext::oomed() which reports whether Skia has seen a GL_OUT_OF_MEMORY
|
||||||
|
error from Open GL [ES] or VK_ERROR_OUT_OF_*_MEMORY from Vulkan.
|
||||||
|
https://review.skia.org/298216
|
||||||
|
|
||||||
* Add option on SkSurface::flush to pass in a GrBackendSurfaceMutableState which
|
* Add option on SkSurface::flush to pass in a GrBackendSurfaceMutableState which
|
||||||
we will set the gpu backend surface to be at the end of the flush.
|
we will set the gpu backend surface to be at the end of the flush.
|
||||||
|
@ -103,6 +103,7 @@ tests_sources = [
|
|||||||
"$_tests/GrCCPRTest.cpp",
|
"$_tests/GrCCPRTest.cpp",
|
||||||
"$_tests/GrContextAbandonTest.cpp",
|
"$_tests/GrContextAbandonTest.cpp",
|
||||||
"$_tests/GrContextFactoryTest.cpp",
|
"$_tests/GrContextFactoryTest.cpp",
|
||||||
|
"$_tests/GrContextOOM.cpp",
|
||||||
"$_tests/GrFinishedFlushTest.cpp",
|
"$_tests/GrFinishedFlushTest.cpp",
|
||||||
"$_tests/GrMemoryPoolTest.cpp",
|
"$_tests/GrMemoryPoolTest.cpp",
|
||||||
"$_tests/GrMeshTest.cpp",
|
"$_tests/GrMeshTest.cpp",
|
||||||
|
@ -151,6 +151,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool abandoned() override;
|
bool abandoned() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the underlying 3D API reported an out-of-memory error. If this returns true it is
|
||||||
|
* reset and will return false until another out-of-memory error is reported by the 3D API. If
|
||||||
|
* the context is abandoned then this will report false.
|
||||||
|
*
|
||||||
|
* Currently this is implemented for:
|
||||||
|
*
|
||||||
|
* OpenGL [ES] - Note that client calls to glGetError() may swallow GL_OUT_OF_MEMORY errors and
|
||||||
|
* therefore hide the error from Skia. Also, it is not advised to use this in combination with
|
||||||
|
* enabling GrContextOptions::fSkipGLErrorChecks. That option may prevent GrContext from ever
|
||||||
|
* checking the GL context for OOM.
|
||||||
|
*
|
||||||
|
* Vulkan - Reports true if VK_ERROR_OUT_OF_HOST_MEMORY or VK_ERROR_OUT_OF_DEVICE_MEMORY has
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
bool oomed();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is similar to abandonContext() however the underlying 3D context is not yet lost and
|
* This is similar to abandonContext() however the underlying 3D context is not yet lost and
|
||||||
* the GrContext will cleanup all allocated resources before returning. After returning it will
|
* the GrContext will cleanup all allocated resources before returning. After returning it will
|
||||||
|
@ -261,6 +261,11 @@ struct SK_API GrContextOptions {
|
|||||||
*/
|
*/
|
||||||
bool fClearAllTextures = false;
|
bool fClearAllTextures = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Randomly generate a (false) GL_OUT_OF_MEMORY error
|
||||||
|
*/
|
||||||
|
bool fRandomGLOOM = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include or exclude specific GPU path renderers.
|
* Include or exclude specific GPU path renderers.
|
||||||
*/
|
*/
|
||||||
|
@ -49,6 +49,13 @@ struct SK_API GrGLInterface : public SkRefCnt {
|
|||||||
private:
|
private:
|
||||||
typedef SkRefCnt INHERITED;
|
typedef SkRefCnt INHERITED;
|
||||||
|
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
// This is here to avoid having our debug code that checks for a GL error after most GL calls
|
||||||
|
// accidentally swallow an OOM that should be reported.
|
||||||
|
mutable bool fOOMed = false;
|
||||||
|
bool fSuppressErrorLogging = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GrGLInterface();
|
GrGLInterface();
|
||||||
|
|
||||||
@ -57,6 +64,19 @@ public:
|
|||||||
// extensions.
|
// extensions.
|
||||||
bool validate() const;
|
bool validate() const;
|
||||||
|
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
GrGLenum checkError(const char* location, const char* call) const;
|
||||||
|
bool checkAndResetOOMed() const;
|
||||||
|
void suppressErrorLogging();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if GR_TEST_UTILS
|
||||||
|
GrGLInterface(const GrGLInterface& that)
|
||||||
|
: fStandard(that.fStandard)
|
||||||
|
, fExtensions(that.fExtensions)
|
||||||
|
, fFunctions(that.fFunctions) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Indicates the type of GL implementation
|
// Indicates the type of GL implementation
|
||||||
union {
|
union {
|
||||||
GrGLStandard fStandard;
|
GrGLStandard fStandard;
|
||||||
|
@ -167,6 +167,8 @@ bool GrContext::abandoned() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GrContext::oomed() { return fGpu ? fGpu->checkAndResetOOMed() : false; }
|
||||||
|
|
||||||
void GrContext::resetGLTextureBindings() {
|
void GrContext::resetGLTextureBindings() {
|
||||||
if (this->abandoned() || this->backend() != GrBackendApi::kOpenGL) {
|
if (this->abandoned() || this->backend() != GrBackendApi::kOpenGL) {
|
||||||
return;
|
return;
|
||||||
|
@ -715,6 +715,14 @@ bool GrGpu::submitToGpu(bool syncCpu) {
|
|||||||
return submitted;
|
return submitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GrGpu::checkAndResetOOMed() {
|
||||||
|
if (fOOMed) {
|
||||||
|
fOOMed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void GrGpu::callSubmittedProcs(bool success) {
|
void GrGpu::callSubmittedProcs(bool success) {
|
||||||
for (int i = 0; i < fSubmittedProcs.count(); ++i) {
|
for (int i = 0; i < fSubmittedProcs.count(); ++i) {
|
||||||
fSubmittedProcs[i].fProc(fSubmittedProcs[i].fContext, success);
|
fSubmittedProcs[i].fProc(fSubmittedProcs[i].fContext, success);
|
||||||
|
@ -388,6 +388,12 @@ public:
|
|||||||
|
|
||||||
virtual void checkFinishProcs() = 0;
|
virtual void checkFinishProcs() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if we detected an OOM from the underlying 3D API and if so returns true and resets
|
||||||
|
* the internal OOM state to false. Otherwise, returns false.
|
||||||
|
*/
|
||||||
|
bool checkAndResetOOMed();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put this texture in a safe and known state for use across multiple GrContexts. Depending on
|
* Put this texture in a safe and known state for use across multiple GrContexts. Depending on
|
||||||
* the backend, this may return a GrSemaphore. If so, other contexts should wait on that
|
* the backend, this may return a GrSemaphore. If so, other contexts should wait on that
|
||||||
@ -721,6 +727,8 @@ protected:
|
|||||||
void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
|
void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
|
||||||
uint32_t mipLevels = 1) const;
|
uint32_t mipLevels = 1) const;
|
||||||
|
|
||||||
|
void setOOMed() { fOOMed = true; }
|
||||||
|
|
||||||
typedef SkTInternalLList<GrStagingBuffer> StagingBufferList;
|
typedef SkTInternalLList<GrStagingBuffer> StagingBufferList;
|
||||||
const StagingBufferList& availableStagingBuffers() { return fAvailableStagingBuffers; }
|
const StagingBufferList& availableStagingBuffers() { return fAvailableStagingBuffers; }
|
||||||
const StagingBufferList& activeStagingBuffers() { return fActiveStagingBuffers; }
|
const StagingBufferList& activeStagingBuffers() { return fActiveStagingBuffers; }
|
||||||
@ -884,6 +892,8 @@ private:
|
|||||||
};
|
};
|
||||||
SkSTArray<4, SubmittedProc> fSubmittedProcs;
|
SkSTArray<4, SubmittedProc> fSubmittedProcs;
|
||||||
|
|
||||||
|
bool fOOMed = false;
|
||||||
|
|
||||||
friend class GrPathRendering;
|
friend class GrPathRendering;
|
||||||
typedef SkRefCnt INHERITED;
|
typedef SkRefCnt INHERITED;
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,13 @@
|
|||||||
#include "src/gpu/dawn/GrDawnGpu.h"
|
#include "src/gpu/dawn/GrDawnGpu.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if GR_TEST_UTILS
|
||||||
|
# include "include/utils/SkRandom.h"
|
||||||
|
# if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
|
||||||
|
# include <sanitizer/lsan_interface.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SK_DISABLE_REDUCE_OPLIST_SPLITTING
|
#ifdef SK_DISABLE_REDUCE_OPLIST_SPLITTING
|
||||||
static const bool kDefaultReduceOpsTaskSplitting = false;
|
static const bool kDefaultReduceOpsTaskSplitting = false;
|
||||||
#else
|
#else
|
||||||
@ -135,10 +142,50 @@ sk_sp<GrContext> GrContext::MakeGL() {
|
|||||||
return MakeGL(nullptr, defaultOptions);
|
return MakeGL(nullptr, defaultOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GR_TEST_UTILS
|
||||||
|
GrGLFunction<GrGLGetErrorFn> make_get_error_with_random_oom(GrGLFunction<GrGLGetErrorFn> original) {
|
||||||
|
// A SkRandom and a GrGLFunction<GrGLGetErrorFn> are too big to be captured by a
|
||||||
|
// GrGLFunction<GrGLGetError> (surprise, surprise). So we make a context object and
|
||||||
|
// capture that by pointer. However, GrGLFunction doesn't support calling a destructor
|
||||||
|
// on the thing it captures. So we leak the context.
|
||||||
|
struct GetErrorContext {
|
||||||
|
SkRandom fRandom;
|
||||||
|
GrGLFunction<GrGLGetErrorFn> fGetError;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto errorContext = new GetErrorContext;
|
||||||
|
|
||||||
|
#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
|
||||||
|
__lsan_ignore_object(errorContext);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
errorContext->fGetError = original;
|
||||||
|
|
||||||
|
return GrGLFunction<GrGLGetErrorFn>([errorContext]() {
|
||||||
|
GrGLenum error = errorContext->fGetError();
|
||||||
|
if (error == GR_GL_NO_ERROR && (errorContext->fRandom.nextU() % 300) == 0) {
|
||||||
|
error = GR_GL_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
sk_sp<GrContext> GrContext::MakeGL(sk_sp<const GrGLInterface> glInterface,
|
sk_sp<GrContext> GrContext::MakeGL(sk_sp<const GrGLInterface> glInterface,
|
||||||
const GrContextOptions& options) {
|
const GrContextOptions& options) {
|
||||||
sk_sp<GrContext> context(new GrLegacyDirectContext(GrBackendApi::kOpenGL, options));
|
sk_sp<GrContext> context(new GrLegacyDirectContext(GrBackendApi::kOpenGL, options));
|
||||||
|
#if GR_TEST_UTILS
|
||||||
|
if (options.fRandomGLOOM) {
|
||||||
|
auto copy = sk_make_sp<GrGLInterface>(*glInterface);
|
||||||
|
copy->fFunctions.fGetError =
|
||||||
|
make_get_error_with_random_oom(glInterface->fFunctions.fGetError);
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
// Suppress logging GL errors since we'll be synthetically generating them.
|
||||||
|
copy->suppressErrorLogging();
|
||||||
|
#endif
|
||||||
|
glInterface = std::move(copy);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
context->fGpu = GrGLGpu::Make(std::move(glInterface), options, context.get());
|
context->fGpu = GrGLGpu::Make(std::move(glInterface), options, context.get());
|
||||||
if (!context->init()) {
|
if (!context->init()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -14,15 +14,17 @@
|
|||||||
#define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
|
#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_CALL_RET(RET, X) GR_GL_CALL_RET(this->glGpu()->glInterface(), RET, X)
|
||||||
|
|
||||||
#if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
|
#define GL_ALLOC_CALL(call) \
|
||||||
#define CLEAR_ERROR_BEFORE_ALLOC(iface) GrGLClearErr(iface)
|
[&] { \
|
||||||
#define GL_ALLOC_CALL(iface, call) GR_GL_CALL_NOERRCHECK(iface, call)
|
if (this->glGpu()->glCaps().skipErrorChecks()) { \
|
||||||
#define CHECK_ALLOC_ERROR(iface) GR_GL_GET_ERROR(iface)
|
GR_GL_CALL(this->glGpu()->glInterface(), call); \
|
||||||
#else
|
return static_cast<GrGLenum>(GR_GL_NO_ERROR); \
|
||||||
#define CLEAR_ERROR_BEFORE_ALLOC(iface)
|
} else { \
|
||||||
#define GL_ALLOC_CALL(iface, call) GR_GL_CALL(iface, call)
|
this->glGpu()->clearErrorsAndCheckForOOM(); \
|
||||||
#define CHECK_ALLOC_ERROR(iface) GR_GL_NO_ERROR
|
GR_GL_CALL_NOERRCHECK(this->glGpu()->glInterface(), call); \
|
||||||
#endif
|
return this->glGpu()->getErrorAndCheckForOOM(); \
|
||||||
|
} \
|
||||||
|
}()
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
#define VALIDATE() this->validate()
|
#define VALIDATE() this->validate()
|
||||||
@ -109,13 +111,8 @@ GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType,
|
|||||||
GL_CALL(GenBuffers(1, &fBufferID));
|
GL_CALL(GenBuffers(1, &fBufferID));
|
||||||
if (fBufferID) {
|
if (fBufferID) {
|
||||||
GrGLenum target = gpu->bindBuffer(fIntendedType, this);
|
GrGLenum target = gpu->bindBuffer(fIntendedType, this);
|
||||||
CLEAR_ERROR_BEFORE_ALLOC(gpu->glInterface());
|
GrGLenum error = GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)size, data, fUsage));
|
||||||
// make sure driver can allocate memory for this buffer
|
if (error != GR_GL_NO_ERROR) {
|
||||||
GL_ALLOC_CALL(gpu->glInterface(), BufferData(target,
|
|
||||||
(GrGLsizeiptr) size,
|
|
||||||
data,
|
|
||||||
fUsage));
|
|
||||||
if (CHECK_ALLOC_ERROR(gpu->glInterface()) != GR_GL_NO_ERROR) {
|
|
||||||
GL_CALL(DeleteBuffers(1, &fBufferID));
|
GL_CALL(DeleteBuffers(1, &fBufferID));
|
||||||
fBufferID = 0;
|
fBufferID = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -182,7 +179,11 @@ void GrGLBuffer::onMap() {
|
|||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
// Let driver know it can discard the old data
|
// Let driver know it can discard the old data
|
||||||
if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
|
if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
|
||||||
GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
GrGLenum error =
|
||||||
|
GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
||||||
|
if (error != GR_GL_NO_ERROR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
|
GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
|
||||||
@ -192,7 +193,10 @@ void GrGLBuffer::onMap() {
|
|||||||
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
|
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
|
||||||
// Make sure the GL buffer size agrees with fDesc before mapping.
|
// Make sure the GL buffer size agrees with fDesc before mapping.
|
||||||
if (fGLSizeInBytes != this->size()) {
|
if (fGLSizeInBytes != this->size()) {
|
||||||
GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
||||||
|
if (error != GR_GL_NO_ERROR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GrGLbitfield access;
|
GrGLbitfield access;
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
@ -211,7 +215,10 @@ void GrGLBuffer::onMap() {
|
|||||||
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
|
GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
|
||||||
// Make sure the GL buffer size agrees with fDesc before mapping.
|
// Make sure the GL buffer size agrees with fDesc before mapping.
|
||||||
if (fGLSizeInBytes != this->size()) {
|
if (fGLSizeInBytes != this->size()) {
|
||||||
GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
GrGLenum error = GL_ALLOC_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
||||||
|
if (error != GR_GL_NO_ERROR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->size(),
|
GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->size(),
|
||||||
readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
|
readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
|
||||||
@ -266,7 +273,11 @@ bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
|
|||||||
|
|
||||||
if (this->glCaps().useBufferDataNullHint()) {
|
if (this->glCaps().useBufferDataNullHint()) {
|
||||||
if (this->size() == srcSizeInBytes) {
|
if (this->size() == srcSizeInBytes) {
|
||||||
GL_CALL(BufferData(target, (GrGLsizeiptr) srcSizeInBytes, src, fUsage));
|
GrGLenum error =
|
||||||
|
GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
|
||||||
|
if (error != GR_GL_NO_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Before we call glBufferSubData we give the driver a hint using
|
// Before we call glBufferSubData we give the driver a hint using
|
||||||
// glBufferData with nullptr. This makes the old buffer contents
|
// glBufferData with nullptr. This makes the old buffer contents
|
||||||
@ -275,7 +286,11 @@ bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
|
|||||||
// assign a different allocation for the new contents to avoid
|
// assign a different allocation for the new contents to avoid
|
||||||
// flushing the gpu past draws consuming the old contents.
|
// flushing the gpu past draws consuming the old contents.
|
||||||
// TODO I think we actually want to try calling bufferData here
|
// TODO I think we actually want to try calling bufferData here
|
||||||
GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
|
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));
|
GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
|
||||||
}
|
}
|
||||||
fGLSizeInBytes = this->size();
|
fGLSizeInBytes = this->size();
|
||||||
@ -283,7 +298,11 @@ bool GrGLBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
|
|||||||
// Note that we're cheating on the size here. Currently no methods
|
// Note that we're cheating on the size here. Currently no methods
|
||||||
// allow a partial update that preserves contents of non-updated
|
// allow a partial update that preserves contents of non-updated
|
||||||
// portions of the buffer (map() does a glBufferData(..size, nullptr..))
|
// portions of the buffer (map() does a glBufferData(..size, nullptr..))
|
||||||
GL_CALL(BufferData(target, srcSizeInBytes, src, fUsage));
|
GrGLenum error =
|
||||||
|
GL_ALLOC_CALL(BufferData(target, (GrGLsizeiptr)srcSizeInBytes, src, fUsage));
|
||||||
|
if (error != GR_GL_NO_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
fGLSizeInBytes = srcSizeInBytes;
|
fGLSizeInBytes = srcSizeInBytes;
|
||||||
}
|
}
|
||||||
VALIDATE();
|
VALIDATE();
|
||||||
|
@ -50,9 +50,9 @@
|
|||||||
GR_GL_CALL(this->glInterface(), call); \
|
GR_GL_CALL(this->glInterface(), call); \
|
||||||
return static_cast<GrGLenum>(GR_GL_NO_ERROR); \
|
return static_cast<GrGLenum>(GR_GL_NO_ERROR); \
|
||||||
} else { \
|
} else { \
|
||||||
GrGLClearErr(this->glInterface()); \
|
this->clearErrorsAndCheckForOOM(); \
|
||||||
GR_GL_CALL_NOERRCHECK(this->glInterface(), call); \
|
GR_GL_CALL_NOERRCHECK(this->glInterface(), call); \
|
||||||
return GR_GL_GET_ERROR(this->glInterface()); \
|
return this->getErrorAndCheckForOOM(); \
|
||||||
} \
|
} \
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -333,7 +333,11 @@ GrGLGpu::GrGLGpu(std::unique_ptr<GrGLContext> ctx, GrContext* context)
|
|||||||
, fStencilClearFBOID(0)
|
, fStencilClearFBOID(0)
|
||||||
, fFinishCallbacks(this) {
|
, fFinishCallbacks(this) {
|
||||||
SkASSERT(fGLContext);
|
SkASSERT(fGLContext);
|
||||||
GrGLClearErr(this->glInterface());
|
// Clear errors so we don't get confused whether we caused an error.
|
||||||
|
this->clearErrorsAndCheckForOOM();
|
||||||
|
// Toss out any pre-existing OOM that was hanging around before we got started.
|
||||||
|
this->checkAndResetOOMed();
|
||||||
|
|
||||||
fCaps = sk_ref_sp(fGLContext->caps());
|
fCaps = sk_ref_sp(fGLContext->caps());
|
||||||
|
|
||||||
fHWTextureUnitBindings.reset(this->numTextureUnits());
|
fHWTextureUnitBindings.reset(this->numTextureUnits());
|
||||||
@ -3862,6 +3866,9 @@ bool GrGLGpu::onSubmitToGpu(bool syncCpu) {
|
|||||||
// See if any previously inserted finish procs are good to go.
|
// See if any previously inserted finish procs are good to go.
|
||||||
fFinishCallbacks.check();
|
fFinishCallbacks.check();
|
||||||
}
|
}
|
||||||
|
if (!this->glCaps().skipErrorChecks()) {
|
||||||
|
this->clearErrorsAndCheckForOOM();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3959,6 +3966,23 @@ void GrGLGpu::checkFinishProcs() {
|
|||||||
fFinishCallbacks.check();
|
fFinishCallbacks.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrGLGpu::clearErrorsAndCheckForOOM() {
|
||||||
|
while (this->getErrorAndCheckForOOM() != GR_GL_NO_ERROR) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
GrGLenum GrGLGpu::getErrorAndCheckForOOM() {
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
if (this->glInterface()->checkAndResetOOMed()) {
|
||||||
|
this->setOOMed();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
GrGLenum error = this->fGLContext->glInterface()->fFunctions.fGetError();
|
||||||
|
if (error == GR_GL_OUT_OF_MEMORY) {
|
||||||
|
this->setOOMed();
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
void GrGLGpu::deleteSync(GrGLsync sync) const {
|
void GrGLGpu::deleteSync(GrGLsync sync) const {
|
||||||
if (this->glCaps().fenceType() == GrGLCaps::FenceType::kNVFence) {
|
if (this->glCaps().fenceType() == GrGLCaps::FenceType::kNVFence) {
|
||||||
GrGLuint nvFence = SkToUInt(reinterpret_cast<intptr_t>(sync));
|
GrGLuint nvFence = SkToUInt(reinterpret_cast<intptr_t>(sync));
|
||||||
|
@ -169,6 +169,11 @@ public:
|
|||||||
|
|
||||||
void checkFinishProcs() override;
|
void checkFinishProcs() override;
|
||||||
|
|
||||||
|
// Calls glGetError() until no errors are reported. Also looks for OOMs.
|
||||||
|
void clearErrorsAndCheckForOOM();
|
||||||
|
// Calls glGetError() once and returns the result. Also looks for an OOM.
|
||||||
|
GrGLenum getErrorAndCheckForOOM();
|
||||||
|
|
||||||
std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
|
std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
|
||||||
|
|
||||||
void deleteSync(GrGLsync) const;
|
void deleteSync(GrGLsync) const;
|
||||||
|
@ -19,6 +19,54 @@ GrGLInterface::GrGLInterface() {
|
|||||||
fStandard = kNone_GrGLStandard;
|
fStandard = kNone_GrGLStandard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
static const char* get_error_string(GrGLenum err) {
|
||||||
|
switch (err) {
|
||||||
|
case GR_GL_NO_ERROR:
|
||||||
|
return "";
|
||||||
|
case GR_GL_INVALID_ENUM:
|
||||||
|
return "Invalid Enum";
|
||||||
|
case GR_GL_INVALID_VALUE:
|
||||||
|
return "Invalid Value";
|
||||||
|
case GR_GL_INVALID_OPERATION:
|
||||||
|
return "Invalid Operation";
|
||||||
|
case GR_GL_OUT_OF_MEMORY:
|
||||||
|
return "Out of Memory";
|
||||||
|
case GR_GL_CONTEXT_LOST:
|
||||||
|
return "Context Lost";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
GrGLenum GrGLInterface::checkError(const char* location, const char* call) const {
|
||||||
|
GrGLenum error = fFunctions.fGetError();
|
||||||
|
if (error != GR_GL_NO_ERROR && !fSuppressErrorLogging) {
|
||||||
|
SkDebugf("---- glGetError 0x%x(%s)", error, get_error_string(error));
|
||||||
|
if (location) {
|
||||||
|
SkDebugf(" at\n\t%s", location);
|
||||||
|
}
|
||||||
|
if (call) {
|
||||||
|
SkDebugf("\n\t\t%s", call);
|
||||||
|
}
|
||||||
|
SkDebugf("\n");
|
||||||
|
if (error == GR_GL_OUT_OF_MEMORY) {
|
||||||
|
fOOMed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GrGLInterface::checkAndResetOOMed() const {
|
||||||
|
if (fOOMed) {
|
||||||
|
fOOMed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrGLInterface::suppressErrorLogging() { fSuppressErrorLogging = true; }
|
||||||
|
#endif
|
||||||
|
|
||||||
#define RETURN_FALSE_INTERFACE \
|
#define RETURN_FALSE_INTERFACE \
|
||||||
SkDEBUGF("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); \
|
SkDEBUGF("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); \
|
||||||
return false
|
return false
|
||||||
|
@ -12,46 +12,6 @@
|
|||||||
#include "src/gpu/gl/GrGLUtil.h"
|
#include "src/gpu/gl/GrGLUtil.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void GrGLClearErr(const GrGLInterface* gl) {
|
|
||||||
while (GR_GL_NO_ERROR != gl->fFunctions.fGetError()) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
const char *get_error_string(uint32_t err) {
|
|
||||||
switch (err) {
|
|
||||||
case GR_GL_NO_ERROR:
|
|
||||||
return "";
|
|
||||||
case GR_GL_INVALID_ENUM:
|
|
||||||
return "Invalid Enum";
|
|
||||||
case GR_GL_INVALID_VALUE:
|
|
||||||
return "Invalid Value";
|
|
||||||
case GR_GL_INVALID_OPERATION:
|
|
||||||
return "Invalid Operation";
|
|
||||||
case GR_GL_OUT_OF_MEMORY:
|
|
||||||
return "Out of Memory";
|
|
||||||
case GR_GL_CONTEXT_LOST:
|
|
||||||
return "Context Lost";
|
|
||||||
}
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrGLCheckErr(const GrGLInterface* gl,
|
|
||||||
const char* location,
|
|
||||||
const char* call) {
|
|
||||||
uint32_t err = GR_GL_GET_ERROR(gl);
|
|
||||||
if (GR_GL_NO_ERROR != err) {
|
|
||||||
SkDebugf("---- glGetError 0x%x(%s)", err, get_error_string(err));
|
|
||||||
if (location) {
|
|
||||||
SkDebugf(" at\n\t%s", location);
|
|
||||||
}
|
|
||||||
if (call) {
|
|
||||||
SkDebugf("\n\t\t%s", call);
|
|
||||||
}
|
|
||||||
SkDebugf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#if GR_GL_LOG_CALLS
|
#if GR_GL_LOG_CALLS
|
||||||
|
@ -257,23 +257,25 @@ void GrGLCheckErr(const GrGLInterface* gl,
|
|||||||
const char* location,
|
const char* location,
|
||||||
const char* call);
|
const char* call);
|
||||||
|
|
||||||
void GrGLClearErr(const GrGLInterface* gl);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Macros for using GrGLInterface to make GL calls
|
* Macros for using GrGLInterface to make GL calls
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// internal macro to conditionally call glGetError based on compile-time and
|
// Conditionally checks glGetError based on compile-time and run-time flags.
|
||||||
// run-time flags.
|
|
||||||
#if GR_GL_CHECK_ERROR
|
#if GR_GL_CHECK_ERROR
|
||||||
extern bool gCheckErrorGL;
|
extern bool gCheckErrorGL;
|
||||||
#define GR_GL_CHECK_ERROR_IMPL(IFACE, X) \
|
#define GR_GL_CHECK_ERROR_IMPL(IFACE, X) \
|
||||||
if (gCheckErrorGL) \
|
do { \
|
||||||
GrGLCheckErr(IFACE, GR_FILE_AND_LINE_STR, #X)
|
if (gCheckErrorGL) { \
|
||||||
|
IFACE->checkError(GR_FILE_AND_LINE_STR, #X); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
#else
|
#else
|
||||||
#define GR_GL_CHECK_ERROR_IMPL(IFACE, X)
|
#define GR_GL_CHECK_ERROR_IMPL(IFACE, X) \
|
||||||
|
do { \
|
||||||
|
} while (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// internal macro to conditionally log the gl call using SkDebugf based on
|
// internal macro to conditionally log the gl call using SkDebugf based on
|
||||||
@ -316,9 +318,6 @@ void GrGLClearErr(const GrGLInterface* gl);
|
|||||||
GR_GL_LOG_CALLS_IMPL(X); \
|
GR_GL_LOG_CALLS_IMPL(X); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
// call glGetError without doing a redundant error check or logging.
|
|
||||||
#define GR_GL_GET_ERROR(IFACE) (IFACE)->fFunctions.fGetError()
|
|
||||||
|
|
||||||
static constexpr GrGLFormat GrGLFormatFromGLEnum(GrGLenum glFormat) {
|
static constexpr GrGLFormat GrGLFormatFromGLEnum(GrGLenum glFormat) {
|
||||||
switch (glFormat) {
|
switch (glFormat) {
|
||||||
case GR_GL_RGBA8: return GrGLFormat::kRGBA8;
|
case GR_GL_RGBA8: return GrGLFormat::kRGBA8;
|
||||||
|
@ -275,11 +275,11 @@ sk_sp<GrGLProgram> GrGLProgramBuilder::finalize(const GrGLPrecompiledProgram* pr
|
|||||||
if (!reader.isValid()) {
|
if (!reader.isValid()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GrGLClearErr(this->gpu()->glInterface());
|
this->gpu()->clearErrorsAndCheckForOOM();
|
||||||
GR_GL_CALL_NOERRCHECK(this->gpu()->glInterface(),
|
GR_GL_CALL_NOERRCHECK(this->gpu()->glInterface(),
|
||||||
ProgramBinary(programID, binaryFormat,
|
ProgramBinary(programID, binaryFormat,
|
||||||
const_cast<void*>(binary), length));
|
const_cast<void*>(binary), length));
|
||||||
if (GR_GL_GET_ERROR(this->gpu()->glInterface()) == GR_GL_NO_ERROR) {
|
if (this->gpu()->getErrorAndCheckForOOM() == GR_GL_NO_ERROR) {
|
||||||
if (checkLinked) {
|
if (checkLinked) {
|
||||||
cached = this->checkLinkStatus(programID, errorHandler, nullptr, nullptr);
|
cached = this->checkLinkStatus(programID, errorHandler, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -171,6 +171,8 @@ public:
|
|||||||
const SkIRect& bounds, bool forSecondaryCB);
|
const SkIRect& bounds, bool forSecondaryCB);
|
||||||
void endRenderPass(GrRenderTarget* target, GrSurfaceOrigin origin, const SkIRect& bounds);
|
void endRenderPass(GrRenderTarget* target, GrSurfaceOrigin origin, const SkIRect& bounds);
|
||||||
|
|
||||||
|
using GrGpu::setOOMed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum SyncQueue {
|
enum SyncQueue {
|
||||||
kForce_SyncQueue,
|
kForce_SyncQueue,
|
||||||
|
@ -21,25 +21,31 @@ class GrVkGpu;
|
|||||||
// makes a Vk call on the interface
|
// makes a Vk call on the interface
|
||||||
#define GR_VK_CALL(IFACE, X) (IFACE)->fFunctions.f##X
|
#define GR_VK_CALL(IFACE, X) (IFACE)->fFunctions.f##X
|
||||||
|
|
||||||
#define GR_VK_CALL_RESULT(GPU, RESULT, X) \
|
#define GR_VK_CALL_RESULT(GPU, RESULT, X) \
|
||||||
do { \
|
do { \
|
||||||
(RESULT) = GR_VK_CALL(GPU->vkInterface(), X); \
|
(RESULT) = GR_VK_CALL(GPU->vkInterface(), X); \
|
||||||
SkASSERT(VK_SUCCESS == RESULT || VK_ERROR_DEVICE_LOST == RESULT); \
|
SkASSERT(VK_SUCCESS == RESULT || VK_ERROR_DEVICE_LOST == RESULT); \
|
||||||
if (RESULT != VK_SUCCESS && !GPU->isDeviceLost()) { \
|
if (RESULT != VK_SUCCESS && !GPU->isDeviceLost()) { \
|
||||||
SkDebugf("Failed vulkan call. Error: %d\n", RESULT); \
|
SkDebugf("Failed vulkan call. Error: %d," #X "\n", RESULT); \
|
||||||
} \
|
} \
|
||||||
if (VK_ERROR_DEVICE_LOST == RESULT) { \
|
if (RESULT == VK_ERROR_DEVICE_LOST) { \
|
||||||
GPU->setDeviceLost(); \
|
GPU->setDeviceLost(); \
|
||||||
} \
|
} else if (RESULT == VK_ERROR_OUT_OF_HOST_MEMORY || \
|
||||||
} while(false)
|
RESULT == VK_ERROR_OUT_OF_DEVICE_MEMORY) { \
|
||||||
|
GPU->setOOMed(); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#define GR_VK_CALL_RESULT_NOCHECK(GPU, RESULT, X) \
|
#define GR_VK_CALL_RESULT_NOCHECK(GPU, RESULT, X) \
|
||||||
do { \
|
do { \
|
||||||
(RESULT) = GR_VK_CALL(GPU->vkInterface(), X); \
|
(RESULT) = GR_VK_CALL(GPU->vkInterface(), X); \
|
||||||
if (VK_ERROR_DEVICE_LOST == RESULT) { \
|
if (RESULT == VK_ERROR_DEVICE_LOST) { \
|
||||||
GPU->setDeviceLost(); \
|
GPU->setDeviceLost(); \
|
||||||
} \
|
} else if (RESULT == VK_ERROR_OUT_OF_HOST_MEMORY || \
|
||||||
} while(false)
|
RESULT == VK_ERROR_OUT_OF_DEVICE_MEMORY) { \
|
||||||
|
GPU->setOOMed(); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
// same as GR_VK_CALL but checks for success
|
// same as GR_VK_CALL but checks for success
|
||||||
#define GR_VK_CALL_ERRCHECK(GPU, X) \
|
#define GR_VK_CALL_ERRCHECK(GPU, X) \
|
||||||
|
53
tests/GrContextOOM.cpp
Normal file
53
tests/GrContextOOM.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "include/core/SkCanvas.h"
|
||||||
|
#include "include/core/SkImageInfo.h"
|
||||||
|
#include "include/core/SkPaint.h"
|
||||||
|
#include "include/core/SkRect.h"
|
||||||
|
#include "include/core/SkSurface.h"
|
||||||
|
#include "include/gpu/GrContext.h"
|
||||||
|
#include "tests/Test.h"
|
||||||
|
|
||||||
|
DEF_GPUTEST(GrContext_oomed, reporter, originalOptions) {
|
||||||
|
GrContextOptions options = originalOptions;
|
||||||
|
options.fRandomGLOOM = true;
|
||||||
|
options.fSkipGLErrorChecks = GrContextOptions::Enable::kNo;
|
||||||
|
sk_gpu_test::GrContextFactory factory(options);
|
||||||
|
for (int ct = 0; ct < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++ct) {
|
||||||
|
auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(ct);
|
||||||
|
GrContext* context = factory.get(contextType);
|
||||||
|
if (!context) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (context->backend() != GrBackendApi::kOpenGL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
||||||
|
for (int run = 0; run < 20; ++run) {
|
||||||
|
bool oomed = false;
|
||||||
|
for (int i = 0; i < 500; ++i) {
|
||||||
|
// Make sure we're actually making a significant number of GL calls and not just
|
||||||
|
// issuing a small number calls by reusing scratch resources created in a previous
|
||||||
|
// iteration.
|
||||||
|
context->freeGpuResources();
|
||||||
|
auto surf =
|
||||||
|
SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 1, nullptr);
|
||||||
|
SkPaint paint;
|
||||||
|
surf->getCanvas()->drawRect(SkRect::MakeLTRB(100, 100, 2000, 2000), paint);
|
||||||
|
surf->flushAndSubmit();
|
||||||
|
if ((oomed = context->oomed())) {
|
||||||
|
REPORTER_ASSERT(reporter, !context->oomed(), "oomed() wasn't cleared");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!oomed) {
|
||||||
|
ERRORF(reporter, "Did not OOM on %dth run.", run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -217,7 +217,7 @@ bool EGLTestHelper::init(skiatest::Reporter* reporter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool EGLTestHelper::importHardwareBuffer(skiatest::Reporter* reporter, AHardwareBuffer* buffer) {
|
bool EGLTestHelper::importHardwareBuffer(skiatest::Reporter* reporter, AHardwareBuffer* buffer) {
|
||||||
GrGLClearErr(fGLCtx->gl());
|
while (fGLCtx->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
|
||||||
|
|
||||||
EGLClientBuffer eglClientBuffer = fEGLGetNativeClientBufferANDROID(buffer);
|
EGLClientBuffer eglClientBuffer = fEGLGetNativeClientBufferANDROID(buffer);
|
||||||
EGLint eglAttribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
EGLint eglAttribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||||
@ -237,15 +237,14 @@ bool EGLTestHelper::importHardwareBuffer(skiatest::Reporter* reporter, AHardware
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GR_GL_CALL_NOERRCHECK(fGLCtx->gl(), BindTexture(GR_GL_TEXTURE_2D, fTexID));
|
GR_GL_CALL_NOERRCHECK(fGLCtx->gl(), BindTexture(GR_GL_TEXTURE_2D, fTexID));
|
||||||
if (GR_GL_GET_ERROR(fGLCtx->gl()) != GR_GL_NO_ERROR) {
|
if (fGLCtx->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
||||||
ERRORF(reporter, "Failed to bind GL Texture");
|
ERRORF(reporter, "Failed to bind GL Texture");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fImage);
|
fEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fImage);
|
||||||
GLenum status = GL_NO_ERROR;
|
if (GrGLenum error = fGLCtx->gl()->fFunctions.fGetError(); error != GR_GL_NO_ERROR) {
|
||||||
if ((status = glGetError()) != GL_NO_ERROR) {
|
ERRORF(reporter, "EGLImageTargetTexture2DOES failed (%#x)", (int) error);
|
||||||
ERRORF(reporter, "EGLImageTargetTexture2DOES failed (%#x)", (int) status);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +375,7 @@ GrEGLImage ANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const {
|
|||||||
void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const { fDestroyImage(fDisplay, image); }
|
void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const { fDestroyImage(fDisplay, image); }
|
||||||
|
|
||||||
GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
|
GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
|
||||||
GrGLClearErr(this->gl());
|
while (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
|
||||||
if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
|
if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -391,12 +391,12 @@ GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
|
GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
|
||||||
if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
|
if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
||||||
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
|
glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
|
||||||
if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
|
if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
||||||
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -375,7 +375,7 @@ void EGLGLTestContext::destroyEGLImage(GrEGLImage image) const {
|
|||||||
|
|
||||||
GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
|
GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
|
||||||
#ifdef SK_GL
|
#ifdef SK_GL
|
||||||
GrGLClearErr(this->gl());
|
while (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
|
||||||
if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
|
if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -392,12 +392,12 @@ GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
|
GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
|
||||||
if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
|
if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
||||||
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
|
glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
|
||||||
if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
|
if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
||||||
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -234,6 +234,54 @@ GrGLInterface::GrGLInterface() {
|
|||||||
fStandard = kNone_GrGLStandard;
|
fStandard = kNone_GrGLStandard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GR_GL_CHECK_ERROR
|
||||||
|
static const char* get_error_string(GrGLenum err) {
|
||||||
|
switch (err) {
|
||||||
|
case GR_GL_NO_ERROR:
|
||||||
|
return "";
|
||||||
|
case GR_GL_INVALID_ENUM:
|
||||||
|
return "Invalid Enum";
|
||||||
|
case GR_GL_INVALID_VALUE:
|
||||||
|
return "Invalid Value";
|
||||||
|
case GR_GL_INVALID_OPERATION:
|
||||||
|
return "Invalid Operation";
|
||||||
|
case GR_GL_OUT_OF_MEMORY:
|
||||||
|
return "Out of Memory";
|
||||||
|
case GR_GL_CONTEXT_LOST:
|
||||||
|
return "Context Lost";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
GrGLenum GrGLInterface::checkError(const char* location, const char* call) const {
|
||||||
|
GrGLenum error = fFunctions.fGetError();
|
||||||
|
if (error != GR_GL_NO_ERROR && !fSuppressErrorLogging) {
|
||||||
|
SkDebugf("---- glGetError 0x%x(%s)", error, get_error_string(error));
|
||||||
|
if (location) {
|
||||||
|
SkDebugf(" at\n\t%s", location);
|
||||||
|
}
|
||||||
|
if (call) {
|
||||||
|
SkDebugf("\n\t\t%s", call);
|
||||||
|
}
|
||||||
|
SkDebugf("\n");
|
||||||
|
if (error == GR_GL_OUT_OF_MEMORY) {
|
||||||
|
fOOMed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GrGLInterface::checkAndResetOOMed() const {
|
||||||
|
if (fOOMed) {
|
||||||
|
fOOMed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrGLInterface::suppressErrorLogging() { fSuppressErrorLogging = true; }
|
||||||
|
#endif
|
||||||
|
|
||||||
#define RETURN_FALSE_INTERFACE \
|
#define RETURN_FALSE_INTERFACE \
|
||||||
SkDEBUGF("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); \
|
SkDEBUGF("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); \
|
||||||
return false
|
return false
|
||||||
|
@ -317,3 +317,11 @@
|
|||||||
fun:_Z12GrClearImageRK11GrImageInfoPvm8SkRGBA4fIL11SkAlphaType3EE
|
fun:_Z12GrClearImageRK11GrImageInfoPvm8SkRGBA4fIL11SkAlphaType3EE
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
make_get_error_with_random_oom
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: definite
|
||||||
|
...
|
||||||
|
fun:_Z30make_get_error_with_random_oom12GrGLFunctionIFjvEE
|
||||||
|
...
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user