f0e04f0c31
This also now requires that when using a vulkan GrContext, the backing VkDevice, Queue, and Instance must be alive when the GrContext is first destroyed or abandoned. Additionally any GrBackendTextures created from the GrContext must be deleted before destroying or abandoning the GrContext. Bug: skia:9603 Change-Id: Ibf194d2ffdcddd61e34214d7d59a2d9a33c880e5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257921 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Greg Daniel <egdaniel@google.com>
321 lines
12 KiB
C++
321 lines
12 KiB
C++
|
|
/*
|
|
* Copyright 2014 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "src/gpu/GrContextPriv.h"
|
|
#include "tools/gpu/GrContextFactory.h"
|
|
#ifdef SK_GL
|
|
#include "tools/gpu/gl/GLTestContext.h"
|
|
#endif
|
|
|
|
#if SK_ANGLE
|
|
#include "tools/gpu/gl/angle/GLTestContext_angle.h"
|
|
#endif
|
|
#include "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.h"
|
|
#ifdef SK_VULKAN
|
|
#include "tools/gpu/vk/VkTestContext.h"
|
|
#endif
|
|
#ifdef SK_METAL
|
|
#include "tools/gpu/mtl/MtlTestContext.h"
|
|
#endif
|
|
#ifdef SK_DAWN
|
|
#include "tools/gpu/dawn/DawnTestContext.h"
|
|
#endif
|
|
#include "src/gpu/GrCaps.h"
|
|
#include "src/gpu/gl/GrGLGpu.h"
|
|
#include "tools/gpu/mock/MockTestContext.h"
|
|
|
|
#if defined(SK_BUILD_FOR_WIN) && defined(SK_ENABLE_DISCRETE_GPU)
|
|
extern "C" {
|
|
// NVIDIA documents that the presence and value of this symbol programmatically enable the high
|
|
// performance GPU in laptops with switchable graphics.
|
|
// https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
|
|
// From testing, including this symbol, even if it is set to 0, we still get the NVIDIA GPU.
|
|
_declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
|
|
|
// AMD has a similar mechanism, although I don't have an AMD laptop, so this is untested.
|
|
// https://community.amd.com/thread/169965
|
|
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|
}
|
|
#endif
|
|
|
|
namespace sk_gpu_test {
|
|
GrContextFactory::GrContextFactory() { }
|
|
|
|
GrContextFactory::GrContextFactory(const GrContextOptions& opts)
|
|
: fGlobalOptions(opts) {
|
|
}
|
|
|
|
GrContextFactory::~GrContextFactory() {
|
|
this->destroyContexts();
|
|
}
|
|
|
|
void GrContextFactory::destroyContexts() {
|
|
// We must delete the test contexts in reverse order so that any child context is finished and
|
|
// deleted before a parent context. This relies on the fact that when we make a new context we
|
|
// append it to the end of fContexts array.
|
|
// TODO: Look into keeping a dependency dag for contexts and deletion order
|
|
for (int i = fContexts.count() - 1; i >= 0; --i) {
|
|
Context& context = fContexts[i];
|
|
SkScopeExit restore(nullptr);
|
|
if (context.fTestContext) {
|
|
restore = context.fTestContext->makeCurrentAndAutoRestore();
|
|
}
|
|
if (!context.fGrContext->unique()) {
|
|
context.fGrContext->releaseResourcesAndAbandonContext();
|
|
context.fAbandoned = true;
|
|
}
|
|
context.fGrContext->unref();
|
|
delete context.fTestContext;
|
|
}
|
|
fContexts.reset();
|
|
}
|
|
|
|
void GrContextFactory::abandonContexts() {
|
|
// We must abandon the test contexts in reverse order so that any child context is finished and
|
|
// abandoned before a parent context. This relies on the fact that when we make a new context we
|
|
// append it to the end of fContexts array.
|
|
// TODO: Look into keeping a dependency dag for contexts and deletion order
|
|
for (int i = fContexts.count() - 1; i >= 0; --i) {
|
|
Context& context = fContexts[i];
|
|
if (!context.fAbandoned) {
|
|
if (context.fTestContext) {
|
|
auto restore = context.fTestContext->makeCurrentAndAutoRestore();
|
|
context.fTestContext->testAbandon();
|
|
}
|
|
bool requiresEarlyAbandon = (context.fGrContext->backend() == GrBackendApi::kVulkan);
|
|
if (requiresEarlyAbandon) {
|
|
context.fGrContext->abandonContext();
|
|
}
|
|
if (context.fTestContext) {
|
|
delete(context.fTestContext);
|
|
context.fTestContext = nullptr;
|
|
}
|
|
if (!requiresEarlyAbandon) {
|
|
context.fGrContext->abandonContext();
|
|
}
|
|
context.fAbandoned = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GrContextFactory::releaseResourcesAndAbandonContexts() {
|
|
// We must abandon the test contexts in reverse order so that any child context is finished and
|
|
// abandoned before a parent context. This relies on the fact that when we make a new context we
|
|
// append it to the end of fContexts array.
|
|
// TODO: Look into keeping a dependency dag for contexts and deletion order
|
|
for (int i = fContexts.count() - 1; i >= 0; --i) {
|
|
Context& context = fContexts[i];
|
|
SkScopeExit restore(nullptr);
|
|
if (!context.fAbandoned) {
|
|
if (context.fTestContext) {
|
|
restore = context.fTestContext->makeCurrentAndAutoRestore();
|
|
}
|
|
context.fGrContext->releaseResourcesAndAbandonContext();
|
|
if (context.fTestContext) {
|
|
delete context.fTestContext;
|
|
context.fTestContext = nullptr;
|
|
}
|
|
context.fAbandoned = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
GrContext* GrContextFactory::get(ContextType type, ContextOverrides overrides) {
|
|
return this->getContextInfo(type, overrides).grContext();
|
|
}
|
|
|
|
ContextInfo GrContextFactory::getContextInfoInternal(ContextType type, ContextOverrides overrides,
|
|
GrContext* shareContext, uint32_t shareIndex) {
|
|
// (shareIndex != 0) -> (shareContext != nullptr)
|
|
SkASSERT((shareIndex == 0) || (shareContext != nullptr));
|
|
|
|
for (int i = 0; i < fContexts.count(); ++i) {
|
|
Context& context = fContexts[i];
|
|
if (context.fType == type &&
|
|
context.fOverrides == overrides &&
|
|
context.fShareContext == shareContext &&
|
|
context.fShareIndex == shareIndex &&
|
|
!context.fAbandoned) {
|
|
context.fTestContext->makeCurrent();
|
|
return ContextInfo(context.fType, context.fTestContext, context.fGrContext,
|
|
context.fOptions);
|
|
}
|
|
}
|
|
|
|
// If we're trying to create a context in a share group, find the master context
|
|
Context* masterContext = nullptr;
|
|
if (shareContext) {
|
|
for (int i = 0; i < fContexts.count(); ++i) {
|
|
if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
|
|
masterContext = &fContexts[i];
|
|
break;
|
|
}
|
|
}
|
|
SkASSERT(masterContext && masterContext->fType == type);
|
|
}
|
|
|
|
std::unique_ptr<TestContext> testCtx;
|
|
GrBackendApi backend = ContextTypeBackend(type);
|
|
switch (backend) {
|
|
#ifdef SK_GL
|
|
case GrBackendApi::kOpenGL: {
|
|
GLTestContext* glShareContext = masterContext
|
|
? static_cast<GLTestContext*>(masterContext->fTestContext) : nullptr;
|
|
GLTestContext* glCtx;
|
|
switch (type) {
|
|
case kGL_ContextType:
|
|
glCtx = CreatePlatformGLTestContext(kGL_GrGLStandard, glShareContext);
|
|
break;
|
|
case kGLES_ContextType:
|
|
glCtx = CreatePlatformGLTestContext(kGLES_GrGLStandard, glShareContext);
|
|
break;
|
|
#if SK_ANGLE
|
|
case kANGLE_D3D9_ES2_ContextType:
|
|
glCtx = MakeANGLETestContext(ANGLEBackend::kD3D9, ANGLEContextVersion::kES2,
|
|
glShareContext).release();
|
|
break;
|
|
case kANGLE_D3D11_ES2_ContextType:
|
|
glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES2,
|
|
glShareContext).release();
|
|
break;
|
|
case kANGLE_D3D11_ES3_ContextType:
|
|
glCtx = MakeANGLETestContext(ANGLEBackend::kD3D11, ANGLEContextVersion::kES3,
|
|
glShareContext).release();
|
|
break;
|
|
case kANGLE_GL_ES2_ContextType:
|
|
glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES2,
|
|
glShareContext).release();
|
|
break;
|
|
case kANGLE_GL_ES3_ContextType:
|
|
glCtx = MakeANGLETestContext(ANGLEBackend::kOpenGL, ANGLEContextVersion::kES3,
|
|
glShareContext).release();
|
|
break;
|
|
#endif
|
|
#ifndef SK_NO_COMMAND_BUFFER
|
|
case kCommandBuffer_ContextType:
|
|
glCtx = CommandBufferGLTestContext::Create(glShareContext);
|
|
break;
|
|
#endif
|
|
default:
|
|
return ContextInfo();
|
|
}
|
|
if (!glCtx) {
|
|
return ContextInfo();
|
|
}
|
|
testCtx.reset(glCtx);
|
|
break;
|
|
}
|
|
#endif // SK_GL
|
|
#ifdef SK_VULKAN
|
|
case GrBackendApi::kVulkan: {
|
|
VkTestContext* vkSharedContext = masterContext
|
|
? static_cast<VkTestContext*>(masterContext->fTestContext) : nullptr;
|
|
SkASSERT(kVulkan_ContextType == type);
|
|
testCtx.reset(CreatePlatformVkTestContext(vkSharedContext));
|
|
if (!testCtx) {
|
|
return ContextInfo();
|
|
}
|
|
|
|
#ifdef SK_GL
|
|
// There is some bug (either in Skia or the NV Vulkan driver) where VkDevice
|
|
// destruction will hang occaisonally. For some reason having an existing GL
|
|
// context fixes this.
|
|
if (!fSentinelGLContext) {
|
|
fSentinelGLContext.reset(CreatePlatformGLTestContext(kGL_GrGLStandard));
|
|
if (!fSentinelGLContext) {
|
|
fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef SK_METAL
|
|
case GrBackendApi::kMetal: {
|
|
MtlTestContext* mtlSharedContext = masterContext
|
|
? static_cast<MtlTestContext*>(masterContext->fTestContext) : nullptr;
|
|
SkASSERT(kMetal_ContextType == type);
|
|
testCtx.reset(CreatePlatformMtlTestContext(mtlSharedContext));
|
|
if (!testCtx) {
|
|
return ContextInfo();
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef SK_DAWN
|
|
case GrBackendApi::kDawn: {
|
|
DawnTestContext* dawnSharedContext = masterContext
|
|
? static_cast<DawnTestContext*>(masterContext->fTestContext) : nullptr;
|
|
testCtx.reset(CreatePlatformDawnTestContext(dawnSharedContext));
|
|
if (!testCtx) {
|
|
return ContextInfo();
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case GrBackendApi::kMock: {
|
|
TestContext* sharedContext = masterContext ? masterContext->fTestContext : nullptr;
|
|
SkASSERT(kMock_ContextType == type);
|
|
testCtx.reset(CreateMockTestContext(sharedContext));
|
|
if (!testCtx) {
|
|
return ContextInfo();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return ContextInfo();
|
|
}
|
|
|
|
SkASSERT(testCtx && testCtx->backend() == backend);
|
|
GrContextOptions grOptions = fGlobalOptions;
|
|
if (ContextOverrides::kAvoidStencilBuffers & overrides) {
|
|
grOptions.fAvoidStencilBuffers = true;
|
|
}
|
|
sk_sp<GrContext> grCtx;
|
|
{
|
|
auto restore = testCtx->makeCurrentAndAutoRestore();
|
|
grCtx = testCtx->makeGrContext(grOptions);
|
|
}
|
|
if (!grCtx.get()) {
|
|
return ContextInfo();
|
|
}
|
|
|
|
// We must always add new contexts by pushing to the back so that when we delete them we delete
|
|
// them in reverse order in which they were made.
|
|
Context& context = fContexts.push_back();
|
|
context.fBackend = backend;
|
|
context.fTestContext = testCtx.release();
|
|
context.fGrContext = SkRef(grCtx.get());
|
|
context.fType = type;
|
|
context.fOverrides = overrides;
|
|
context.fAbandoned = false;
|
|
context.fShareContext = shareContext;
|
|
context.fShareIndex = shareIndex;
|
|
context.fOptions = grOptions;
|
|
context.fTestContext->makeCurrent();
|
|
return ContextInfo(context.fType, context.fTestContext, context.fGrContext, context.fOptions);
|
|
}
|
|
|
|
ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides overrides) {
|
|
return this->getContextInfoInternal(type, overrides, nullptr, 0);
|
|
}
|
|
|
|
ContextInfo GrContextFactory::getSharedContextInfo(GrContext* shareContext, uint32_t shareIndex) {
|
|
SkASSERT(shareContext);
|
|
for (int i = 0; i < fContexts.count(); ++i) {
|
|
if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
|
|
return this->getContextInfoInternal(fContexts[i].fType, fContexts[i].fOverrides,
|
|
shareContext, shareIndex);
|
|
}
|
|
}
|
|
|
|
return ContextInfo();
|
|
}
|
|
|
|
} // namespace sk_gpu_test
|