diff options
Diffstat (limited to 'src')
39 files changed, 4898 insertions, 35 deletions
diff --git a/src/gpu/GrContextFactory.cpp b/src/gpu/GrContextFactory.cpp new file mode 100755 index 0000000000..00ae1226e8 --- /dev/null +++ b/src/gpu/GrContextFactory.cpp @@ -0,0 +1,158 @@ +/* + * 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 "GrContextFactory.h" + +#if SK_ANGLE + #include "gl/angle/SkANGLEGLContext.h" +#endif +#if SK_COMMAND_BUFFER + #include "gl/command_buffer/SkCommandBufferGLContext.h" +#endif +#include "gl/debug/SkDebugGLContext.h" +#if SK_MESA + #include "gl/mesa/SkMesaGLContext.h" +#endif +#if SK_VULKAN +#include "vk/GrVkBackendContext.h" +#endif +#include "gl/SkGLContext.h" +#include "gl/SkNullGLContext.h" +#include "gl/GrGLGpu.h" +#include "GrCaps.h" + +GrContextFactory::GrContextFactory() { } + +GrContextFactory::GrContextFactory(const GrContextOptions& opts) + : fGlobalOptions(opts) { +} + +GrContextFactory::~GrContextFactory() { + this->destroyContexts(); +} + +void GrContextFactory::destroyContexts() { + for (Context& context : fContexts) { + if (context.fGLContext) { + context.fGLContext->makeCurrent(); + } + if (!context.fGrContext->unique()) { + context.fGrContext->abandonContext(); + } + context.fGrContext->unref(); + delete(context.fGLContext); + } + fContexts.reset(); +} + +void GrContextFactory::abandonContexts() { + for (Context& context : fContexts) { + if (context.fGLContext) { + context.fGLContext->makeCurrent(); + context.fGLContext->testAbandon(); + delete(context.fGLContext); + context.fGLContext = nullptr; + } + context.fGrContext->abandonContext(); + } +} + +GrContextFactory::ContextInfo GrContextFactory::getContextInfo(GLContextType type, + GLContextOptions options) { + for (int i = 0; i < fContexts.count(); ++i) { + Context& context = fContexts[i]; + if (!context.fGLContext) { + continue; + } + if (context.fType == type && + context.fOptions == options) { + context.fGLContext->makeCurrent(); + return ContextInfo(context.fGrContext, context.fGLContext); + } + } + SkAutoTDelete<SkGLContext> glCtx; + SkAutoTUnref<GrContext> grCtx; + switch (type) { + case kNative_GLContextType: + glCtx.reset(SkCreatePlatformGLContext(kNone_GrGLStandard)); + break; + case kGL_GLContextType: + glCtx.reset(SkCreatePlatformGLContext(kGL_GrGLStandard)); + break; + case kGLES_GLContextType: + glCtx.reset(SkCreatePlatformGLContext(kGLES_GrGLStandard)); + break; +#if SK_ANGLE +#ifdef SK_BUILD_FOR_WIN + case kANGLE_GLContextType: + glCtx.reset(SkANGLEGLContext::CreateDirectX()); + break; +#endif + case kANGLE_GL_GLContextType: + glCtx.reset(SkANGLEGLContext::CreateOpenGL()); + break; +#endif +#if SK_COMMAND_BUFFER + case kCommandBuffer_GLContextType: + glCtx.reset(SkCommandBufferGLContext::Create()); + break; +#endif +#if SK_MESA + case kMESA_GLContextType: + glCtx.reset(SkMesaGLContext::Create()); + break; +#endif + case kNull_GLContextType: + glCtx.reset(SkNullGLContext::Create()); + break; + case kDebug_GLContextType: + glCtx.reset(SkDebugGLContext::Create()); + break; + } + if (nullptr == glCtx.get()) { + return ContextInfo(); + } + + SkASSERT(glCtx->isValid()); + + // Block NVPR from non-NVPR types. + SkAutoTUnref<const GrGLInterface> glInterface(SkRef(glCtx->gl())); + if (!(kEnableNVPR_GLContextOptions & options)) { + glInterface.reset(GrGLInterfaceRemoveNVPR(glInterface)); + if (!glInterface) { + return ContextInfo(); + } + } + + glCtx->makeCurrent(); +#ifdef SK_VULKAN + if (kEnableNVPR_GLContextOptions & options) { + return ContextInfo(); + } else { + GrBackendContext p3dctx = reinterpret_cast<GrBackendContext>(GrVkBackendContext::Create()); + grCtx.reset(GrContext::Create(kVulkan_GrBackend, p3dctx, fGlobalOptions)); + } +#else + GrBackendContext p3dctx = reinterpret_cast<GrBackendContext>(glInterface.get()); + grCtx.reset(GrContext::Create(kOpenGL_GrBackend, p3dctx, fGlobalOptions)); +#endif + if (!grCtx.get()) { + return ContextInfo(); + } + if (kEnableNVPR_GLContextOptions & options) { + if (!grCtx->caps()->shaderCaps()->pathRenderingSupport()) { + return ContextInfo(); + } + } + + Context& context = fContexts.push_back(); + context.fGLContext = glCtx.release(); + context.fGrContext = SkRef(grCtx.get()); + context.fType = type; + context.fOptions = options; + return ContextInfo(context.fGrContext, context.fGLContext); +} diff --git a/src/gpu/GrContextFactory.h b/src/gpu/GrContextFactory.h new file mode 100644 index 0000000000..7afa3108c6 --- /dev/null +++ b/src/gpu/GrContextFactory.h @@ -0,0 +1,144 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrContextFactory_DEFINED +#define GrContextFactory_DEFINED + +#include "GrContext.h" +#include "GrContextOptions.h" + +#include "gl/SkGLContext.h" +#include "SkTArray.h" + +/** + * This is a simple class that is useful in test apps that use different + * GrContexts backed by different types of GL contexts. It manages creating the + * GL context and a GrContext that uses it. The GL/Gr contexts persist until the + * factory is destroyed (though the caller can always grab a ref on the returned + * Gr and GL contexts to make them outlive the factory). + */ +class GrContextFactory : SkNoncopyable { +public: + enum GLContextType { + kNative_GLContextType, //! OpenGL or OpenGL ES context. + kGL_GLContextType, //! OpenGL context. + kGLES_GLContextType, //! OpenGL ES context. +#if SK_ANGLE +#ifdef SK_BUILD_FOR_WIN + kANGLE_GLContextType, //! ANGLE on DirectX OpenGL ES context. +#endif + kANGLE_GL_GLContextType, //! ANGLE on OpenGL OpenGL ES context. +#endif +#if SK_COMMAND_BUFFER + kCommandBuffer_GLContextType, //! Chromium command buffer OpenGL ES context. +#endif +#if SK_MESA + kMESA_GLContextType, //! MESA OpenGL context +#endif + kNull_GLContextType, //! Non-rendering OpenGL mock context. + kDebug_GLContextType, //! Non-rendering, state verifying OpenGL context. + kLastGLContextType = kDebug_GLContextType + }; + + static const int kGLContextTypeCnt = kLastGLContextType + 1; + + /** + * Options for GL context creation. For historical and testing reasons the options will default + * to not using GL_NV_path_rendering extension even when the driver supports it. + */ + enum GLContextOptions { + kNone_GLContextOptions = 0, + kEnableNVPR_GLContextOptions = 0x1, + }; + + static bool IsRenderingGLContext(GLContextType type) { + switch (type) { + case kNull_GLContextType: + case kDebug_GLContextType: + return false; + default: + return true; + } + } + + static const char* GLContextTypeName(GLContextType type) { + switch (type) { + case kNative_GLContextType: + return "native"; + case kGL_GLContextType: + return "gl"; + case kGLES_GLContextType: + return "gles"; +#if SK_ANGLE +#ifdef SK_BUILD_FOR_WIN + case kANGLE_GLContextType: + return "angle"; +#endif + case kANGLE_GL_GLContextType: + return "angle-gl"; +#endif +#if SK_COMMAND_BUFFER + case kCommandBuffer_GLContextType: + return "commandbuffer"; +#endif +#if SK_MESA + case kMESA_GLContextType: + return "mesa"; +#endif + case kNull_GLContextType: + return "null"; + case kDebug_GLContextType: + return "debug"; + default: + SkFAIL("Unknown GL Context type."); + } + } + + explicit GrContextFactory(const GrContextOptions& opts); + GrContextFactory(); + + ~GrContextFactory(); + + void destroyContexts(); + void abandonContexts(); + + struct ContextInfo { + ContextInfo() + : fGrContext(nullptr), fGLContext(nullptr) { } + ContextInfo(GrContext* grContext, SkGLContext* glContext) + : fGrContext(grContext), fGLContext(glContext) { } + GrContext* fGrContext; + SkGLContext* fGLContext; //! Valid until the factory destroys it via abandonContexts() or + //! destroyContexts(). + }; + + /** + * Get a context initialized with a type of GL context. It also makes the GL context current. + */ + ContextInfo getContextInfo(GLContextType type, + GLContextOptions options = kNone_GLContextOptions); + /** + * Get a GrContext initialized with a type of GL context. It also makes the GL context current. + */ + GrContext* get(GLContextType type, + GLContextOptions options = kNone_GLContextOptions) { + return this->getContextInfo(type, options).fGrContext; + } + const GrContextOptions& getGlobalOptions() const { return fGlobalOptions; } + +private: + struct Context { + GLContextType fType; + GLContextOptions fOptions; + SkGLContext* fGLContext; + GrContext* fGrContext; + }; + SkTArray<Context, true> fContexts; + const GrContextOptions fGlobalOptions; +}; + +#endif diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp new file mode 100644 index 0000000000..2b6463d853 --- /dev/null +++ b/src/gpu/GrTest.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTest.h" + +#include "GrBatchAtlas.h" +#include "GrContextOptions.h" +#include "GrDrawContextPriv.h" +#include "GrDrawingManager.h" +#include "GrGpuResourceCacheAccess.h" +#include "GrResourceCache.h" + +#include "SkGpuDevice.h" +#include "SkGrPriv.h" +#include "SkString.h" + +#include "text/GrBatchFontCache.h" +#include "text/GrTextBlobCache.h" + +namespace GrTest { +void SetupAlwaysEvictAtlas(GrContext* context) { + // These sizes were selected because they allow each atlas to hold a single plot and will thus + // stress the atlas + int dim = GrBatchAtlas::kGlyphMaxDim; + GrBatchAtlasConfig configs[3]; + configs[kA8_GrMaskFormat].fWidth = dim; + configs[kA8_GrMaskFormat].fHeight = dim; + configs[kA8_GrMaskFormat].fLog2Width = SkNextLog2(dim); + configs[kA8_GrMaskFormat].fLog2Height = SkNextLog2(dim); + configs[kA8_GrMaskFormat].fPlotWidth = dim; + configs[kA8_GrMaskFormat].fPlotHeight = dim; + + configs[kA565_GrMaskFormat].fWidth = dim; + configs[kA565_GrMaskFormat].fHeight = dim; + configs[kA565_GrMaskFormat].fLog2Width = SkNextLog2(dim); + configs[kA565_GrMaskFormat].fLog2Height = SkNextLog2(dim); + configs[kA565_GrMaskFormat].fPlotWidth = dim; + configs[kA565_GrMaskFormat].fPlotHeight = dim; + + configs[kARGB_GrMaskFormat].fWidth = dim; + configs[kARGB_GrMaskFormat].fHeight = dim; + configs[kARGB_GrMaskFormat].fLog2Width = SkNextLog2(dim); + configs[kARGB_GrMaskFormat].fLog2Height = SkNextLog2(dim); + configs[kARGB_GrMaskFormat].fPlotWidth = dim; + configs[kARGB_GrMaskFormat].fPlotHeight = dim; + + context->setTextContextAtlasSizes_ForTesting(configs); +} +}; + +void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target, GrRenderTarget* rt) { + SkASSERT(!fContext); + + fContext.reset(SkRef(ctx)); + fDrawTarget.reset(SkRef(target)); + fRenderTarget.reset(SkRef(rt)); +} + +void GrContext::getTestTarget(GrTestTarget* tar, GrRenderTarget* rt) { + this->flush(); + // We could create a proxy GrDrawTarget that passes through to fGpu until ~GrTextTarget() and + // then disconnects. This would help prevent test writers from mixing using the returned + // GrDrawTarget and regular drawing. We could also assert or fail in GrContext drawing methods + // until ~GrTestTarget(). + if (!rt) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = 32; + desc.fHeight = 32; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fSampleCnt = 0; + + SkAutoTUnref<GrTexture> texture(this->textureProvider()->createTexture( + desc, SkBudgeted::kNo, nullptr, 0)); + if (nullptr == texture) { + return; + } + SkASSERT(nullptr != texture->asRenderTarget()); + rt = texture->asRenderTarget(); + } + + SkAutoTUnref<GrDrawTarget> dt(fDrawingManager->newDrawTarget(rt)); + tar->init(this, dt, rt); +} + +void GrContext::setTextBlobCacheLimit_ForTesting(size_t bytes) { + fTextBlobCache->setBudget(bytes); +} + +void GrContext::setTextContextAtlasSizes_ForTesting(const GrBatchAtlasConfig* configs) { + fBatchFontCache->setAtlasSizes_ForTesting(configs); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrContext::purgeAllUnlockedResources() { + fResourceCache->purgeAllUnlocked(); +} + +void GrContext::resetGpuStats() const { +#if GR_GPU_STATS + fGpu->stats()->reset(); +#endif +} + +void GrContext::dumpCacheStats(SkString* out) const { +#if GR_CACHE_STATS + fResourceCache->dumpStats(out); +#endif +} + +void GrContext::dumpCacheStatsKeyValuePairs(SkTArray<SkString>* keys, + SkTArray<double>* values) const { +#if GR_CACHE_STATS + fResourceCache->dumpStatsKeyValuePairs(keys, values); +#endif +} + +void GrContext::printCacheStats() const { + SkString out; + this->dumpCacheStats(&out); + SkDebugf("%s", out.c_str()); +} + +void GrContext::dumpGpuStats(SkString* out) const { +#if GR_GPU_STATS + return fGpu->stats()->dump(out); +#endif +} + +void GrContext::dumpGpuStatsKeyValuePairs(SkTArray<SkString>* keys, + SkTArray<double>* values) const { +#if GR_GPU_STATS + return fGpu->stats()->dumpKeyValuePairs(keys, values); +#endif +} + +void GrContext::printGpuStats() const { + SkString out; + this->dumpGpuStats(&out); + SkDebugf("%s", out.c_str()); +} + +GrTexture* GrContext::getFontAtlasTexture(GrMaskFormat format) { + GrBatchFontCache* cache = this->getBatchFontCache(); + + return cache->getTexture(format); +} + +void SkGpuDevice::drawTexture(GrTexture* tex, const SkRect& dst, const SkPaint& paint) { + GrPaint grPaint; + SkMatrix mat; + mat.reset(); + if (!SkPaintToGrPaint(this->context(), paint, mat, &grPaint)) { + return; + } + SkMatrix textureMat; + textureMat.reset(); + textureMat[SkMatrix::kMScaleX] = 1.0f/dst.width(); + textureMat[SkMatrix::kMScaleY] = 1.0f/dst.height(); + textureMat[SkMatrix::kMTransX] = -dst.fLeft/dst.width(); + textureMat[SkMatrix::kMTransY] = -dst.fTop/dst.height(); + + grPaint.addColorTextureProcessor(tex, textureMat); + + GrClip clip; + fDrawContext->drawRect(clip, grPaint, mat, dst); +} + + +#if GR_GPU_STATS +void GrGpu::Stats::dump(SkString* out) { + out->appendf("Render Target Binds: %d\n", fRenderTargetBinds); + out->appendf("Shader Compilations: %d\n", fShaderCompilations); + out->appendf("Textures Created: %d\n", fTextureCreates); + out->appendf("Texture Uploads: %d\n", fTextureUploads); + out->appendf("Transfers to Texture: %d\n", fTransfersToTexture); + out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates); + out->appendf("Number of draws: %d\n", fNumDraws); +} + +void GrGpu::Stats::dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values) { + keys->push_back(SkString("render_target_binds")); values->push_back(fRenderTargetBinds); + keys->push_back(SkString("shader_compilations")); values->push_back(fShaderCompilations); + keys->push_back(SkString("texture_uploads")); values->push_back(fTextureUploads); + keys->push_back(SkString("number_of_draws")); values->push_back(fNumDraws); + keys->push_back(SkString("number_of_failed_draws")); values->push_back(fNumFailedDraws); +} + +#endif + +#if GR_CACHE_STATS +void GrResourceCache::getStats(Stats* stats) const { + stats->reset(); + + stats->fTotal = this->getResourceCount(); + stats->fNumNonPurgeable = fNonpurgeableResources.count(); + stats->fNumPurgeable = fPurgeableQueue.count(); + + for (int i = 0; i < fNonpurgeableResources.count(); ++i) { + stats->update(fNonpurgeableResources[i]); + } + for (int i = 0; i < fPurgeableQueue.count(); ++i) { + stats->update(fPurgeableQueue.at(i)); + } +} + +void GrResourceCache::dumpStats(SkString* out) const { + this->validate(); + + Stats stats; + + this->getStats(&stats); + + float countUtilization = (100.f * fBudgetedCount) / fMaxCount; + float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes; + + out->appendf("Budget: %d items %d bytes\n", fMaxCount, (int)fMaxBytes); + out->appendf("\t\tEntry Count: current %d" + " (%d budgeted, %d external(%d borrowed, %d adopted), %d locked, %d scratch %.2g%% full), high %d\n", + stats.fTotal, fBudgetedCount, stats.fExternal, stats.fBorrowed, + stats.fAdopted, stats.fNumNonPurgeable, stats.fScratch, countUtilization, + fHighWaterCount); + out->appendf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n", + SkToInt(fBytes), SkToInt(fBudgetedBytes), byteUtilization, + SkToInt(stats.fUnbudgetedSize), SkToInt(fHighWaterBytes)); +} + +void GrResourceCache::dumpStatsKeyValuePairs(SkTArray<SkString>* keys, + SkTArray<double>* values) const { + this->validate(); + + Stats stats; + this->getStats(&stats); + + keys->push_back(SkString("gpu_cache_purgable_entries")); values->push_back(stats.fNumPurgeable); +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void GrResourceCache::changeTimestamp(uint32_t newTimestamp) { fTimestamp = newTimestamp; } + +/////////////////////////////////////////////////////////////////////////////// + +#define ASSERT_SINGLE_OWNER \ + SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fDrawContext->fSingleOwner);) +#define RETURN_IF_ABANDONED if (fDrawContext->fDrawingManager->abandoned()) { return; } + +void GrDrawContextPriv::testingOnly_drawBatch(const GrPipelineBuilder& pipelineBuilder, + GrDrawBatch* batch) { + ASSERT_SINGLE_OWNER + RETURN_IF_ABANDONED + SkDEBUGCODE(fDrawContext->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fDrawContext->fAuditTrail, "GrDrawContext::testingOnly_drawBatch"); + + fDrawContext->getDrawTarget()->drawBatch(pipelineBuilder, batch); +} + +#undef ASSERT_SINGLE_OWNER +#undef RETURN_IF_ABANDONED + +/////////////////////////////////////////////////////////////////////////////// +// Code for the mock context. It's built on a mock GrGpu class that does nothing. +//// + +#include "GrGpu.h" + +class GrPipeline; + +class MockCaps : public GrCaps { +public: + explicit MockCaps(const GrContextOptions& options) : INHERITED(options) {} + bool isConfigTexturable(GrPixelConfig config) const override { return false; } + bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const override { return false; } +private: + typedef GrCaps INHERITED; +}; + +class MockGpu : public GrGpu { +public: + MockGpu(GrContext* context, const GrContextOptions& options) : INHERITED(context) { + fCaps.reset(new MockCaps(options)); + } + ~MockGpu() override {} + + bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight, size_t rowBytes, + GrPixelConfig readConfig, DrawPreference*, + ReadPixelTempDrawInfo*) override { return false; } + + bool onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height, + GrPixelConfig srcConfig, DrawPreference*, + WritePixelTempDrawInfo*) override { return false; } + + void discard(GrRenderTarget*) override {} + + bool onCopySurface(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) override { return false; }; + + void onGetMultisampleSpecs(GrRenderTarget* rt, + const GrStencilSettings&, + int* effectiveSampleCnt, + SkAutoTDeleteArray<SkPoint>*) override { + *effectiveSampleCnt = rt->desc().fSampleCnt; + } + + bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override { + return false; + } + + void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {}; + +private: + void onResetContext(uint32_t resetBits) override {} + + void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {} + + GrTexture* onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle, + const SkTArray<GrMipLevel>& texels) override { + return nullptr; + } + + GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle, + const SkTArray<GrMipLevel>& texels) override { + return nullptr; + } + + GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&, + GrWrapOwnership) override { return nullptr; } + + GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&, + GrWrapOwnership) override { + return nullptr; + } + + GrRenderTarget* onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&, + GrWrapOwnership) override { + return nullptr; + } + + GrBuffer* onCreateBuffer(GrBufferType, size_t, GrAccessPattern) override { return nullptr; } + + void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override {} + + void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override {} + + void onDraw(const GrPipeline&, + const GrPrimitiveProcessor&, + const GrMesh*, + int meshCount) override {} + + bool onReadPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig, + void* buffer, + size_t rowBytes) override { + return false; + } + + bool onWritePixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, const SkTArray<GrMipLevel>& texels) override { + return false; + } + + bool onTransferPixels(GrSurface* surface, + int left, int top, int width, int height, + GrPixelConfig config, GrBuffer* transferBuffer, + size_t offset, size_t rowBytes) override { + return false; + } + + void onResolveRenderTarget(GrRenderTarget* target) override { return; } + + GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*, + int width, + int height) override { + return nullptr; + } + + void clearStencil(GrRenderTarget* target) override {} + + GrBackendObject createTestingOnlyBackendTexture(void* pixels, int w, int h, + GrPixelConfig config) override { + return 0; + } + bool isTestingOnlyBackendTexture(GrBackendObject ) const override { return false; } + void deleteTestingOnlyBackendTexture(GrBackendObject, bool abandonTexture) override {} + + typedef GrGpu INHERITED; +}; + +GrContext* GrContext::CreateMockContext() { + GrContext* context = new GrContext; + + context->initMockContext(); + return context; +} + +void GrContext::initMockContext() { + GrContextOptions options; + options.fBufferMapThreshold = 0; + SkASSERT(nullptr == fGpu); + fGpu = new MockGpu(this, options); + SkASSERT(fGpu); + this->initCommon(options); + + // We delete these because we want to test the cache starting with zero resources. Also, none of + // these objects are required for any of tests that use this context. TODO: make stop allocating + // resources in the buffer pools. + fDrawingManager->abandon(); +} diff --git a/src/gpu/GrTest.h b/src/gpu/GrTest.h new file mode 100644 index 0000000000..53aaac34e4 --- /dev/null +++ b/src/gpu/GrTest.h @@ -0,0 +1,42 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTest_DEFINED +#define GrTest_DEFINED + +#include "GrContext.h" +#include "GrDrawTarget.h" +#include "gl/GrGLContext.h" + +namespace GrTest { + /** + * Forces the GrContext to use a small atlas which only has room for one plot and will thus + * constantly be evicting entries + */ + void SetupAlwaysEvictAtlas(GrContext*); +}; + +/** TODO Please do not use this if you can avoid it. We are in the process of deleting it. + Allows a test to temporarily draw to a GrDrawTarget owned by a GrContext. Tests that use this + should be careful not to mix using the GrDrawTarget directly and drawing via SkCanvas or + GrContext. In the future this object may provide some guards to prevent this. */ +class GrTestTarget { +public: + GrTestTarget() {}; + + void init(GrContext*, GrDrawTarget*, GrRenderTarget*); + + GrDrawTarget* target() { return fDrawTarget.get(); } + GrResourceProvider* resourceProvider() { return fContext->resourceProvider(); } + +private: + SkAutoTUnref<GrContext> fContext; + SkAutoTUnref<GrDrawTarget> fDrawTarget; + SkAutoTUnref<GrRenderTarget> fRenderTarget; +}; + +#endif diff --git a/src/gpu/gl/GrGLTestInterface.h b/src/gpu/gl/GrGLTestInterface.h index 936d9b8a97..c5f91ee195 100644 --- a/src/gpu/gl/GrGLTestInterface.h +++ b/src/gpu/gl/GrGLTestInterface.h @@ -11,10 +11,6 @@ #include "gl/GrGLInterface.h" #include "GrGLDefines.h" -/** - * Base class for interfaces used for Skia testing. We would like to move this to tools/gpu/gl - * when Chromium is no longer using GrGLCreateNullInterface in its unit testing. - */ class GrGLTestInterface : public GrGLInterface { public: virtual GrGLvoid activeTexture(GrGLenum texture) {} diff --git a/src/gpu/gl/SkGLContext.cpp b/src/gpu/gl/SkGLContext.cpp new file mode 100644 index 0000000000..1f9011fbd9 --- /dev/null +++ b/src/gpu/gl/SkGLContext.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "gl/SkGLContext.h" +#include "GrGLUtil.h" +#include "SkGpuFenceSync.h" + +class SkGLContext::GLFenceSync : public SkGpuFenceSync { +public: + static GLFenceSync* CreateIfSupported(const SkGLContext*); + + SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const override; + bool waitFence(SkPlatformGpuFence fence, bool flush) const override; + void deleteFence(SkPlatformGpuFence fence) const override; + +private: + GLFenceSync() {} + + static const GrGLenum GL_SYNC_GPU_COMMANDS_COMPLETE = 0x9117; + static const GrGLenum GL_WAIT_FAILED = 0x911d; + static const GrGLbitfield GL_SYNC_FLUSH_COMMANDS_BIT = 0x00000001; + + typedef struct __GLsync *GLsync; + + typedef GLsync (GR_GL_FUNCTION_TYPE* GLFenceSyncProc) (GrGLenum, GrGLbitfield); + typedef GrGLenum (GR_GL_FUNCTION_TYPE* GLClientWaitSyncProc) (GLsync, GrGLbitfield, GrGLuint64); + typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GLDeleteSyncProc) (GLsync); + + GLFenceSyncProc fGLFenceSync; + GLClientWaitSyncProc fGLClientWaitSync; + GLDeleteSyncProc fGLDeleteSync; + + typedef SkGpuFenceSync INHERITED; +}; + +SkGLContext::SkGLContext() + : fCurrentFenceIdx(0) { + memset(fFrameFences, 0, sizeof(fFrameFences)); +} + +SkGLContext::~SkGLContext() { + // Subclass should call teardown. +#ifdef SK_DEBUG + for (size_t i = 0; i < SK_ARRAY_COUNT(fFrameFences); i++) { + SkASSERT(0 == fFrameFences[i]); + } +#endif + SkASSERT(nullptr == fGL.get()); + SkASSERT(nullptr == fFenceSync.get()); +} + +void SkGLContext::init(const GrGLInterface* gl, SkGpuFenceSync* fenceSync) { + SkASSERT(!fGL.get()); + fGL.reset(gl); + fFenceSync.reset(fenceSync ? fenceSync : GLFenceSync::CreateIfSupported(this)); +} + +void SkGLContext::teardown() { + if (fFenceSync) { + for (size_t i = 0; i < SK_ARRAY_COUNT(fFrameFences); i++) { + if (fFrameFences[i]) { + fFenceSync->deleteFence(fFrameFences[i]); + fFrameFences[i] = 0; + } + } + fFenceSync.reset(nullptr); + } + + fGL.reset(nullptr); +} + +void SkGLContext::makeCurrent() const { + this->onPlatformMakeCurrent(); +} + +void SkGLContext::swapBuffers() { + this->onPlatformSwapBuffers(); +} + +void SkGLContext::waitOnSyncOrSwap() { + if (!fFenceSync) { + // Fallback on the platform SwapBuffers method for synchronization. This may have no effect. + this->swapBuffers(); + return; + } + + if (fFrameFences[fCurrentFenceIdx]) { + if (!fFenceSync->waitFence(fFrameFences[fCurrentFenceIdx], true)) { + SkDebugf("WARNING: Wait failed for fence sync. Timings might not be accurate.\n"); + } + fFenceSync->deleteFence(fFrameFences[fCurrentFenceIdx]); + } + + fFrameFences[fCurrentFenceIdx] = fFenceSync->insertFence(); + fCurrentFenceIdx = (fCurrentFenceIdx + 1) % SK_ARRAY_COUNT(fFrameFences); +} + +void SkGLContext::testAbandon() { + if (fGL) { + fGL->abandon(); + } + if (fFenceSync) { + memset(fFrameFences, 0, sizeof(fFrameFences)); + } +} + +SkGLContext::GLFenceSync* SkGLContext::GLFenceSync::CreateIfSupported(const SkGLContext* ctx) { + SkAutoTDelete<GLFenceSync> ret(new GLFenceSync); + + if (kGL_GrGLStandard == ctx->gl()->fStandard) { + const GrGLubyte* versionStr; + SK_GL_RET(*ctx, versionStr, GetString(GR_GL_VERSION)); + GrGLVersion version = GrGLGetVersionFromString(reinterpret_cast<const char*>(versionStr)); + if (version < GR_GL_VER(3,2) && !ctx->gl()->hasExtension("GL_ARB_sync")) { + return nullptr; + } + ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>( + ctx->onPlatformGetProcAddress("glFenceSync")); + ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>( + ctx->onPlatformGetProcAddress("glClientWaitSync")); + ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>( + ctx->onPlatformGetProcAddress("glDeleteSync")); + } else { + if (!ctx->gl()->hasExtension("GL_APPLE_sync")) { + return nullptr; + } + ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>( + ctx->onPlatformGetProcAddress("glFenceSyncAPPLE")); + ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>( + ctx->onPlatformGetProcAddress("glClientWaitSyncAPPLE")); + ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>( + ctx->onPlatformGetProcAddress("glDeleteSyncAPPLE")); + } + + if (!ret->fGLFenceSync || !ret->fGLClientWaitSync || !ret->fGLDeleteSync) { + return nullptr; + } + + return ret.release(); +} + +SkPlatformGpuFence SkGLContext::GLFenceSync::insertFence() const { + return fGLFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +bool SkGLContext::GLFenceSync::waitFence(SkPlatformGpuFence fence, bool flush) const { + GLsync glsync = static_cast<GLsync>(fence); + return GL_WAIT_FAILED != fGLClientWaitSync(glsync, flush ? GL_SYNC_FLUSH_COMMANDS_BIT : 0, -1); +} + +void SkGLContext::GLFenceSync::deleteFence(SkPlatformGpuFence fence) const { + GLsync glsync = static_cast<GLsync>(fence); + fGLDeleteSync(glsync); +} + +GrGLint SkGLContext::createTextureRectangle(int width, int height, GrGLenum internalFormat, + GrGLenum externalFormat, GrGLenum externalType, + GrGLvoid* data) { + if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 1)) && + !fGL->fExtensions.has("GL_ARB_texture_rectangle")) { + return 0; + } + + if (GrGLGetGLSLVersion(fGL) < GR_GLSL_VER(1, 40)) { + return 0; + } + + GrGLuint id; + GR_GL_CALL(fGL, GenTextures(1, &id)); + GR_GL_CALL(fGL, BindTexture(GR_GL_TEXTURE_RECTANGLE, id)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MAG_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MIN_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_S, + GR_GL_CLAMP_TO_EDGE)); + GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_T, + GR_GL_CLAMP_TO_EDGE)); + GR_GL_CALL(fGL, TexImage2D(GR_GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, + externalFormat, externalType, data)); + return id; +} diff --git a/src/gpu/gl/SkNullGLContext.cpp b/src/gpu/gl/SkNullGLContext.cpp new file mode 100644 index 0000000000..a2c5066273 --- /dev/null +++ b/src/gpu/gl/SkNullGLContext.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/SkNullGLContext.h" +#include "gl/GrGLInterface.h" + +SkNullGLContext* SkNullGLContext::Create() { + SkNullGLContext* ctx = new SkNullGLContext; + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} + +SkNullGLContext::SkNullGLContext() { + this->init(GrGLCreateNullInterface()); +} + +SkNullGLContext::~SkNullGLContext() { + this->teardown(); +} diff --git a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp new file mode 100644 index 0000000000..48f9d8c8ff --- /dev/null +++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/GrGLInterface.h" +#include "gl/GrGLAssembleInterface.h" +#include "../ports/SkOSLibrary.h" + +#include <EGL/egl.h> + +namespace { +struct Libs { + void* fGLLib; + void* fEGLLib; +}; +} + +static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) { + const Libs* libs = reinterpret_cast<const Libs*>(ctx); + GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name); + if (proc) { + return proc; + } + proc = (GrGLFuncPtr) GetProcedureAddress(libs->fEGLLib, name); + if (proc) { + return proc; + } + return eglGetProcAddress(name); +} + +const GrGLInterface* GrGLCreateANGLEInterface() { + static Libs gLibs = { nullptr, nullptr }; + + if (nullptr == gLibs.fGLLib) { + // We load the ANGLE library and never let it go +#if defined _WIN32 + gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dll"); + gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dll"); +#elif defined SK_BUILD_FOR_MAC + gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dylib"); + gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib"); +#else + gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so"); + gLibs.fEGLLib = DynamicLoadLibrary("libEGL.so"); +#endif + } + + if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) { + // We can't setup the interface correctly w/o the so + return nullptr; + } + + return GrGLAssembleGLESInterface(&gLibs, angle_get_gl_proc); +} diff --git a/src/gpu/gl/angle/SkANGLEGLContext.cpp b/src/gpu/gl/angle/SkANGLEGLContext.cpp new file mode 100644 index 0000000000..c6ea44381f --- /dev/null +++ b/src/gpu/gl/angle/SkANGLEGLContext.cpp @@ -0,0 +1,229 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/angle/SkANGLEGLContext.h" + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "gl/GrGLDefines.h" +#include "gl/GrGLUtil.h" + +#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 +#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 +#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 +#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D + +void* SkANGLEGLContext::GetD3DEGLDisplay(void* nativeDisplay, bool useGLBackend) { + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; + eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if (!eglGetPlatformDisplayEXT) { + return eglGetDisplay(static_cast<EGLNativeDisplayType>(nativeDisplay)); + } + + EGLDisplay display = EGL_NO_DISPLAY; + if (useGLBackend) { + // Try for an ANGLE D3D11 context, fall back to D3D9. + EGLint attribs[3] = { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, + EGL_NONE + }; + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs); + } else { + // Try for an ANGLE D3D11 context, fall back to D3D9, and finally GL. + EGLint attribs[3][3] = { + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_NONE + }, + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, + EGL_NONE + }, + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, + EGL_NONE + } + }; + for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) { + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]); + } + } + return display; +} + +SkANGLEGLContext::SkANGLEGLContext(bool useGLBackend) + : fContext(EGL_NO_CONTEXT) + , fDisplay(EGL_NO_DISPLAY) + , fSurface(EGL_NO_SURFACE) { + + EGLint numConfigs; + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + + fIsGLBackend = useGLBackend; + fDisplay = GetD3DEGLDisplay(EGL_DEFAULT_DISPLAY, useGLBackend); + if (EGL_NO_DISPLAY == fDisplay) { + SkDebugf("Could not create EGL display!"); + return; + } + + EGLint majorVersion; + EGLint minorVersion; + eglInitialize(fDisplay, &majorVersion, &minorVersion); + + EGLConfig surfaceConfig; + eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs); + + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + fContext = eglCreateContext(fDisplay, surfaceConfig, nullptr, contextAttribs); + + + static const EGLint surfaceAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + + fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs); + + eglMakeCurrent(fDisplay, fSurface, fSurface, fContext); + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateANGLEInterface()); + if (nullptr == gl.get()) { + SkDebugf("Could not create ANGLE GL interface!\n"); + this->destroyGLContext(); + return; + } + if (!gl->validate()) { + SkDebugf("Could not validate ANGLE GL interface!\n"); + this->destroyGLContext(); + return; + } + + this->init(gl.release()); +} + +SkANGLEGLContext::~SkANGLEGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +GrEGLImage SkANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const { + if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) { + return GR_EGL_NO_IMAGE; + } + GrEGLImage img; + GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, + GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE, + GR_EGL_NONE }; + // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer. + GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID); + GR_GL_CALL_RET(this->gl(), img, + EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, + attribs)); + return img; +} + +void SkANGLEGLContext::destroyEGLImage(GrEGLImage image) const { + GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image)); +} + +GrGLuint SkANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const { + GrGLClearErr(this->gl()); + if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) { + return 0; + } + typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage); + + EGLImageTargetTexture2DProc glEGLImageTargetTexture2D = + (EGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (!glEGLImageTargetTexture2D) { + return 0; + } + GrGLuint texID; + GR_GL_CALL(this->gl(), GenTextures(1, &texID)); + if (!texID) { + return 0; + } + GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID)); + if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { + GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); + return 0; + } + glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image); + if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { + GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); + return 0; + } + return texID; +} + +SkGLContext* SkANGLEGLContext::createNew() const { +#ifdef SK_BUILD_FOR_WIN + SkGLContext* ctx = fIsGLBackend ? SkANGLEGLContext::CreateOpenGL() + : SkANGLEGLContext::CreateDirectX(); +#else + SkGLContext* ctx = SkANGLEGLContext::CreateOpenGL(); +#endif + if (ctx) { + ctx->makeCurrent(); + } + return ctx; +} + +void SkANGLEGLContext::destroyGLContext() { + if (fDisplay) { + eglMakeCurrent(fDisplay, 0, 0, 0); + + if (fContext) { + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + } + + if (fSurface) { + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + } + + //TODO should we close the display? + fDisplay = EGL_NO_DISPLAY; + } +} + +void SkANGLEGLContext::onPlatformMakeCurrent() const { + if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Could not set the context.\n"); + } +} + +void SkANGLEGLContext::onPlatformSwapBuffers() const { + if (!eglSwapBuffers(fDisplay, fSurface)) { + SkDebugf("Could not complete eglSwapBuffers.\n"); + } +} + +GrGLFuncPtr SkANGLEGLContext::onPlatformGetProcAddress(const char* name) const { + return eglGetProcAddress(name); +} diff --git a/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp b/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp new file mode 100644 index 0000000000..516c155fcc --- /dev/null +++ b/src/gpu/gl/command_buffer/SkCommandBufferGLContext.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkOnce.h" +#include "gl/GrGLInterface.h" +#include "gl/GrGLAssembleInterface.h" +#include "gl/command_buffer/SkCommandBufferGLContext.h" +#include "../ports/SkOSEnvironment.h" +#include "../ports/SkOSLibrary.h" + +#if defined SK_BUILD_FOR_MAC + +// EGL doesn't exist on the mac, so expose what we need to get the command buffer's EGL running. +typedef void *EGLDisplay; +typedef unsigned int EGLBoolean; +typedef void *EGLConfig; +typedef void *EGLSurface; +typedef void *EGLContext; +typedef int32_t EGLint; +typedef void* EGLNativeDisplayType; +typedef void* EGLNativeWindowType; +typedef void (*__eglMustCastToProperFunctionPointerType)(void); +#define EGL_FALSE 0 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_NO_SURFACE ((EGLSurface)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_PBUFFER_BIT 0x0001 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_RED_SIZE 0x3024 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3025 +#define EGL_SAMPLES 0x3031 +#define EGL_SAMPLE_BUFFERS 0x3032 +#define EGL_NONE 0x3038 +#define EGL_WIDTH 0x3057 +#define EGL_HEIGHT 0x3056 + +#else + +#include <EGL/egl.h> + +#endif + +typedef EGLDisplay (*GetDisplayProc)(EGLNativeDisplayType display_id); +typedef EGLBoolean (*InitializeProc)(EGLDisplay dpy, EGLint *major, EGLint *minor); +typedef EGLBoolean (*TerminateProc)(EGLDisplay dpy); +typedef EGLBoolean (*ChooseConfigProc)(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config); +typedef EGLBoolean (*GetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value); +typedef EGLSurface (*CreateWindowSurfaceProc)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list); +typedef EGLSurface (*CreatePbufferSurfaceProc)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list); +typedef EGLBoolean (*DestroySurfaceProc)(EGLDisplay dpy, EGLSurface surface); +typedef EGLContext (*CreateContextProc)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list); +typedef EGLBoolean (*DestroyContextProc)(EGLDisplay dpy, EGLContext ctx); +typedef EGLBoolean (*MakeCurrentProc)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); +typedef EGLBoolean (*SwapBuffersProc)(EGLDisplay dpy, EGLSurface surface); +typedef __eglMustCastToProperFunctionPointerType (*GetProcAddressProc)(const char* procname); + +static GetDisplayProc gfGetDisplay = nullptr; +static InitializeProc gfInitialize = nullptr; +static TerminateProc gfTerminate = nullptr; +static ChooseConfigProc gfChooseConfig = nullptr; +static GetConfigAttrib gfGetConfigAttrib = nullptr; +static CreateWindowSurfaceProc gfCreateWindowSurface = nullptr; +static CreatePbufferSurfaceProc gfCreatePbufferSurface = nullptr; +static DestroySurfaceProc gfDestroySurface = nullptr; +static CreateContextProc gfCreateContext = nullptr; +static DestroyContextProc gfDestroyContext = nullptr; +static MakeCurrentProc gfMakeCurrent = nullptr; +static SwapBuffersProc gfSwapBuffers = nullptr; +static GetProcAddressProc gfGetProcAddress = nullptr; + +static void* gLibrary = nullptr; +static bool gfFunctionsLoadedSuccessfully = false; + +static void load_command_buffer_functions() { + if (!gLibrary) { +#if defined _WIN32 + gLibrary = DynamicLoadLibrary("command_buffer_gles2.dll"); +#elif defined SK_BUILD_FOR_MAC + gLibrary = DynamicLoadLibrary("libcommand_buffer_gles2.dylib"); +#else + gLibrary = DynamicLoadLibrary("libcommand_buffer_gles2.so"); +#endif // defined _WIN32 + if (gLibrary) { + gfGetDisplay = (GetDisplayProc)GetProcedureAddress(gLibrary, "eglGetDisplay"); + gfInitialize = (InitializeProc)GetProcedureAddress(gLibrary, "eglInitialize"); + gfTerminate = (TerminateProc)GetProcedureAddress(gLibrary, "eglTerminate"); + gfChooseConfig = (ChooseConfigProc)GetProcedureAddress(gLibrary, "eglChooseConfig"); + gfGetConfigAttrib = (GetConfigAttrib)GetProcedureAddress(gLibrary, "eglGetConfigAttrib"); + gfCreateWindowSurface = (CreateWindowSurfaceProc)GetProcedureAddress(gLibrary, "eglCreateWindowSurface"); + gfCreatePbufferSurface = (CreatePbufferSurfaceProc)GetProcedureAddress(gLibrary, "eglCreatePbufferSurface"); + gfDestroySurface = (DestroySurfaceProc)GetProcedureAddress(gLibrary, "eglDestroySurface"); + gfCreateContext = (CreateContextProc)GetProcedureAddress(gLibrary, "eglCreateContext"); + gfDestroyContext = (DestroyContextProc)GetProcedureAddress(gLibrary, "eglDestroyContext"); + gfMakeCurrent = (MakeCurrentProc)GetProcedureAddress(gLibrary, "eglMakeCurrent"); + gfSwapBuffers = (SwapBuffersProc)GetProcedureAddress(gLibrary, "eglSwapBuffers"); + gfGetProcAddress = (GetProcAddressProc)GetProcedureAddress(gLibrary, "eglGetProcAddress"); + + gfFunctionsLoadedSuccessfully = gfGetDisplay && gfInitialize && gfTerminate && + gfChooseConfig && gfCreateWindowSurface && + gfCreatePbufferSurface && gfDestroySurface && + gfCreateContext && gfDestroyContext && gfMakeCurrent && + gfSwapBuffers && gfGetProcAddress; + + } + } +} + +static GrGLFuncPtr command_buffer_get_gl_proc(void* ctx, const char name[]) { + if (!gfFunctionsLoadedSuccessfully) { + return nullptr; + } + return gfGetProcAddress(name); +} + +SK_DECLARE_STATIC_ONCE(loadCommandBufferOnce); +void LoadCommandBufferOnce() { + SkOnce(&loadCommandBufferOnce, load_command_buffer_functions); +} + +const GrGLInterface* GrGLCreateCommandBufferInterface() { + LoadCommandBufferOnce(); + if (!gfFunctionsLoadedSuccessfully) { + return nullptr; + } + return GrGLAssembleGLESInterface(gLibrary, command_buffer_get_gl_proc); +} + +SkCommandBufferGLContext::SkCommandBufferGLContext() + : fContext(EGL_NO_CONTEXT) + , fDisplay(EGL_NO_DISPLAY) + , fSurface(EGL_NO_SURFACE) { + + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + + static const EGLint surfaceAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + + initializeGLContext(nullptr, configAttribs, surfaceAttribs); +} + +SkCommandBufferGLContext::SkCommandBufferGLContext(void* nativeWindow, int msaaSampleCount) { + static const EGLint surfaceAttribs[] = { EGL_NONE }; + + EGLint configAttribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_SAMPLE_BUFFERS, 1, + EGL_SAMPLES, msaaSampleCount, + EGL_NONE + }; + if (msaaSampleCount == 0) { + configAttribs[12] = EGL_NONE; + } + + initializeGLContext(nativeWindow, configAttribs, surfaceAttribs); +} + +void SkCommandBufferGLContext::initializeGLContext(void* nativeWindow, const int* configAttribs, + const int* surfaceAttribs) { + LoadCommandBufferOnce(); + if (!gfFunctionsLoadedSuccessfully) { + SkDebugf("Command Buffer: Could not load EGL functions.\n"); + return; + } + + // Make sure CHROMIUM_path_rendering is enabled for NVPR support. + sk_setenv("CHROME_COMMAND_BUFFER_GLES2_ARGS", "--enable-gl-path-rendering"); + fDisplay = gfGetDisplay(EGL_DEFAULT_DISPLAY); + if (EGL_NO_DISPLAY == fDisplay) { + SkDebugf("Command Buffer: Could not create EGL display.\n"); + return; + } + + EGLint majorVersion; + EGLint minorVersion; + if (!gfInitialize(fDisplay, &majorVersion, &minorVersion)) { + SkDebugf("Command Buffer: Could not initialize EGL display.\n"); + this->destroyGLContext(); + return; + } + + EGLint numConfigs; + if (!gfChooseConfig(fDisplay, configAttribs, static_cast<EGLConfig*>(&fConfig), 1, + &numConfigs) || numConfigs != 1) { + SkDebugf("Command Buffer: Could not choose EGL config.\n"); + this->destroyGLContext(); + return; + } + + if (nativeWindow) { + fSurface = gfCreateWindowSurface(fDisplay, + static_cast<EGLConfig>(fConfig), + (EGLNativeWindowType)nativeWindow, + surfaceAttribs); + } else { + fSurface = gfCreatePbufferSurface(fDisplay, + static_cast<EGLConfig>(fConfig), + surfaceAttribs); + } + if (EGL_NO_SURFACE == fSurface) { + SkDebugf("Command Buffer: Could not create EGL surface.\n"); + this->destroyGLContext(); + return; + } + + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + fContext = gfCreateContext(fDisplay, static_cast<EGLConfig>(fConfig), nullptr, contextAttribs); + if (EGL_NO_CONTEXT == fContext) { + SkDebugf("Command Buffer: Could not create EGL context.\n"); + this->destroyGLContext(); + return; + } + + if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Command Buffer: Could not make EGL context current.\n"); + this->destroyGLContext(); + return; + } + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateCommandBufferInterface()); + if (nullptr == gl.get()) { + SkDebugf("Command Buffer: Could not create CommandBuffer GL interface.\n"); + this->destroyGLContext(); + return; + } + if (!gl->validate()) { + SkDebugf("Command Buffer: Could not validate CommandBuffer GL interface.\n"); + this->destroyGLContext(); + return; + } + + this->init(gl.release()); +} + +SkCommandBufferGLContext::~SkCommandBufferGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void SkCommandBufferGLContext::destroyGLContext() { + if (!gfFunctionsLoadedSuccessfully) { + return; + } + if (fDisplay) { + gfMakeCurrent(fDisplay, 0, 0, 0); + + if (fContext) { + gfDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + } + + if (fSurface) { + gfDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + } + + gfTerminate(fDisplay); + fDisplay = EGL_NO_DISPLAY; + } +} + +void SkCommandBufferGLContext::onPlatformMakeCurrent() const { + if (!gfFunctionsLoadedSuccessfully) { + return; + } + if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Command Buffer: Could not make EGL context current.\n"); + } +} + +void SkCommandBufferGLContext::onPlatformSwapBuffers() const { + if (!gfFunctionsLoadedSuccessfully) { + return; + } + if (!gfSwapBuffers(fDisplay, fSurface)) { + SkDebugf("Command Buffer: Could not complete gfSwapBuffers.\n"); + } +} + +GrGLFuncPtr SkCommandBufferGLContext::onPlatformGetProcAddress(const char* name) const { + if (!gfFunctionsLoadedSuccessfully) { + return nullptr; + } + return gfGetProcAddress(name); +} + +void SkCommandBufferGLContext::presentCommandBuffer() { + if (this->gl()) { + this->gl()->fFunctions.fFlush(); + } + + this->onPlatformSwapBuffers(); +} + +bool SkCommandBufferGLContext::makeCurrent() { + return gfMakeCurrent(fDisplay, fSurface, fSurface, fContext) != EGL_FALSE; +} + +int SkCommandBufferGLContext::getStencilBits() { + EGLint result = 0; + gfGetConfigAttrib(fDisplay, static_cast<EGLConfig>(fConfig), EGL_STENCIL_SIZE, &result); + return result; +} + +int SkCommandBufferGLContext::getSampleCount() { + EGLint result = 0; + gfGetConfigAttrib(fDisplay, static_cast<EGLConfig>(fConfig), EGL_SAMPLES, &result); + return result; +} diff --git a/src/gpu/gl/debug/GrBufferObj.cpp b/src/gpu/gl/debug/GrBufferObj.cpp new file mode 100644 index 0000000000..ee0993d6da --- /dev/null +++ b/src/gpu/gl/debug/GrBufferObj.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrBufferObj.h" + +void GrBufferObj::allocate(GrGLsizeiptr size, const GrGLchar *dataPtr) { + GrAlwaysAssert(size >= 0); + + // delete pre-existing data + delete[] fDataPtr; + + fSize = size; + fDataPtr = new GrGLchar[size]; + if (dataPtr) { + memcpy(fDataPtr, dataPtr, fSize); + } + // TODO: w/ no dataPtr the data is unitialized - this could be tracked +} + +void GrBufferObj::deleteAction() { + + // buffers are automatically unmapped when deleted + this->resetMapped(); + + this->INHERITED::deleteAction(); +} diff --git a/src/gpu/gl/debug/GrBufferObj.h b/src/gpu/gl/debug/GrBufferObj.h new file mode 100644 index 0000000000..5b5015be96 --- /dev/null +++ b/src/gpu/gl/debug/GrBufferObj.h @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrBufferObj_DEFINED +#define GrBufferObj_DEFINED + +#include "GrFakeRefObj.h" +#include "../GrGLDefines.h" + +//////////////////////////////////////////////////////////////////////////////// +class GrBufferObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrBufferObj); + +public: + GrBufferObj() + : GrFakeRefObj() + , fDataPtr(nullptr) + , fMapped(false) + , fBound(false) + , fSize(0) + , fUsage(GR_GL_STATIC_DRAW) { + } + virtual ~GrBufferObj() { + delete[] fDataPtr; + } + + void access() { + // cannot access the buffer if it is currently mapped + GrAlwaysAssert(!fMapped); + } + + void setMapped(GrGLintptr offset, GrGLsizeiptr length) { + fMapped = true; + fMappedOffset = offset; + fMappedLength = length; + } + void resetMapped() { fMapped = false; } + bool getMapped() const { return fMapped; } + GrGLintptr getMappedOffset() const { return fMappedOffset; } + GrGLsizeiptr getMappedLength() const { return fMappedLength; } + + void setBound() { fBound = true; } + void resetBound() { fBound = false; } + bool getBound() const { return fBound; } + + void allocate(GrGLsizeiptr size, const GrGLchar *dataPtr); + GrGLsizeiptr getSize() const { return fSize; } + GrGLchar *getDataPtr() { return fDataPtr; } + + void setUsage(GrGLint usage) { fUsage = usage; } + GrGLint getUsage() const { return fUsage; } + + void deleteAction() override; + +protected: +private: + + GrGLchar* fDataPtr; + bool fMapped; // is the buffer object mapped via "glMapBuffer[Range]"? + GrGLintptr fMappedOffset; // the offset of the buffer range that is mapped + GrGLsizeiptr fMappedLength; // the size of the buffer range that is mapped + bool fBound; // is the buffer object bound via "glBindBuffer"? + GrGLsizeiptr fSize; // size in bytes + GrGLint fUsage; // one of: GL_STREAM_DRAW, + // GL_STATIC_DRAW, + // GL_DYNAMIC_DRAW + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrBufferObj_DEFINED diff --git a/src/gpu/gl/debug/GrFBBindableObj.h b/src/gpu/gl/debug/GrFBBindableObj.h new file mode 100644 index 0000000000..2d490ef08b --- /dev/null +++ b/src/gpu/gl/debug/GrFBBindableObj.h @@ -0,0 +1,87 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrFBBindableObj_DEFINED +#define GrFBBindableObj_DEFINED + +#include "SkTDArray.h" +#include "GrFakeRefObj.h" + +//////////////////////////////////////////////////////////////////////////////// +// A common base class for render buffers and textures +class GrFBBindableObj : public GrFakeRefObj { + +public: + GrFBBindableObj() + : GrFakeRefObj() { + } + + virtual ~GrFBBindableObj() { + GrAlwaysAssert(0 == fColorReferees.count()); + GrAlwaysAssert(0 == fDepthReferees.count()); + GrAlwaysAssert(0 == fStencilReferees.count()); + } + + void setColorBound(GrFakeRefObj *referee) { + fColorReferees.append(1, &referee); + } + void resetColorBound(GrFakeRefObj *referee) { + int index = fColorReferees.find(referee); + GrAlwaysAssert(0 <= index); + fColorReferees.removeShuffle(index); + } + bool getColorBound(GrFakeRefObj *referee) const { + int index = fColorReferees.find(referee); + return 0 <= index; + } + bool getColorBound() const { + return 0 != fColorReferees.count(); + } + + void setDepthBound(GrFakeRefObj *referee) { + fDepthReferees.append(1, &referee); + } + void resetDepthBound(GrFakeRefObj *referee) { + int index = fDepthReferees.find(referee); + GrAlwaysAssert(0 <= index); + fDepthReferees.removeShuffle(index); + } + bool getDepthBound(GrFakeRefObj *referee) const { + int index = fDepthReferees.find(referee); + return 0 <= index; + } + bool getDepthBound() const { + return 0 != fDepthReferees.count(); + } + + void setStencilBound(GrFakeRefObj *referee) { + fStencilReferees.append(1, &referee); + } + void resetStencilBound(GrFakeRefObj *referee) { + int index = fStencilReferees.find(referee); + GrAlwaysAssert(0 <= index); + fStencilReferees.removeShuffle(index); + } + bool getStencilBound(GrFakeRefObj *referee) const { + int index = fStencilReferees.find(referee); + return 0 <= index; + } + bool getStencilBound() const { + return 0 != fStencilReferees.count(); + } + + +protected: +private: + SkTDArray<GrFakeRefObj *> fColorReferees; // frame buffers that use this as a color buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D") + SkTDArray<GrFakeRefObj *> fDepthReferees; // frame buffers that use this as a depth buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D") + SkTDArray<GrFakeRefObj *> fStencilReferees; // frame buffers that use this as a stencil buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D") + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrFBBindableObj_DEFINED diff --git a/src/gpu/gl/debug/GrFakeRefObj.h b/src/gpu/gl/debug/GrFakeRefObj.h new file mode 100644 index 0000000000..30580516fa --- /dev/null +++ b/src/gpu/gl/debug/GrFakeRefObj.h @@ -0,0 +1,86 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrFakeRefObj_DEFINED +#define GrFakeRefObj_DEFINED + +#include "SkTypes.h" +#include "gl/GrGLInterface.h" + +//////////////////////////////////////////////////////////////////////////////// +// This object is used to track the OpenGL objects. We don't use real +// reference counting (i.e., we don't free the objects when their ref count +// goes to 0) so that we can detect invalid memory accesses. The refs we +// are tracking in this class are actually OpenGL's references to the objects +// not "ours" +// Each object also gets a unique globally identifying ID +class GrFakeRefObj : SkNoncopyable { +public: + GrFakeRefObj() + : fRef(0) + , fMarkedForDeletion(false) + , fDeleted(false) { + + // source for globally unique IDs - 0 is reserved! + static int fNextID = 0; + + fID = ++fNextID; + } + virtual ~GrFakeRefObj() {}; + + void ref() { + fRef++; + } + void unref() { + fRef--; + GrAlwaysAssert(fRef >= 0); + + // often in OpenGL a given object may still be in use when the + // delete call is made. In these cases the object is marked + // for deletion and then freed when it is no longer in use + if (0 == fRef && fMarkedForDeletion) { + this->deleteAction(); + } + } + int getRefCount() const { return fRef; } + + GrGLuint getID() const { return fID; } + + void setMarkedForDeletion() { fMarkedForDeletion = true; } + bool getMarkedForDeletion() const { return fMarkedForDeletion; } + + bool getDeleted() const { return fDeleted; } + + // The deleteAction fires if the object has been marked for deletion but + // couldn't be deleted earlier due to refs + virtual void deleteAction() { + this->setDeleted(); + } + +protected: +private: + int fRef; // ref count + GrGLuint fID; // globally unique ID + bool fMarkedForDeletion; + // The deleted flag is only set when OpenGL thinks the object is deleted + // It is obviously still allocated w/in this framework + bool fDeleted; + + // setDeleted should only ever appear in the deleteAction method! + void setDeleted() { fDeleted = true; } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Each class derived from GrFakeRefObj should use this macro to add a +// factory creation entry point. This entry point is used by the GrGLDebug +// object to instantiate the various objects +// all globally unique IDs +#define GR_DEFINE_CREATOR(className) \ +public: \ + static GrFakeRefObj *create##className() { return new className; } + +#endif // GrFakeRefObj_DEFINED diff --git a/src/gpu/gl/debug/GrFrameBufferObj.cpp b/src/gpu/gl/debug/GrFrameBufferObj.cpp new file mode 100644 index 0000000000..a8acc3de23 --- /dev/null +++ b/src/gpu/gl/debug/GrFrameBufferObj.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrFrameBufferObj.h" +#include "GrFBBindableObj.h" + +void GrFrameBufferObj::setColor(GrFBBindableObj *buffer) { + if (fColorBuffer) { + // automatically break the binding of the old buffer + GrAlwaysAssert(fColorBuffer->getColorBound(this)); + fColorBuffer->resetColorBound(this); + + GrAlwaysAssert(!fColorBuffer->getDeleted()); + fColorBuffer->unref(); + } + fColorBuffer = buffer; + if (fColorBuffer) { + GrAlwaysAssert(!fColorBuffer->getDeleted()); + fColorBuffer->ref(); + + GrAlwaysAssert(!fColorBuffer->getColorBound(this)); + fColorBuffer->setColorBound(this); + } +} + +void GrFrameBufferObj::setDepth(GrFBBindableObj *buffer) { + if (fDepthBuffer) { + // automatically break the binding of the old buffer + GrAlwaysAssert(fDepthBuffer->getDepthBound(this)); + fDepthBuffer->resetDepthBound(this); + + GrAlwaysAssert(!fDepthBuffer->getDeleted()); + fDepthBuffer->unref(); + } + fDepthBuffer = buffer; + if (fDepthBuffer) { + GrAlwaysAssert(!fDepthBuffer->getDeleted()); + fDepthBuffer->ref(); + + GrAlwaysAssert(!fDepthBuffer->getDepthBound(this)); + fDepthBuffer->setDepthBound(this); + } +} + +void GrFrameBufferObj::setStencil(GrFBBindableObj *buffer) { + if (fStencilBuffer) { + // automatically break the binding of the old buffer + GrAlwaysAssert(fStencilBuffer->getStencilBound(this)); + fStencilBuffer->resetStencilBound(this); + + //GrAlwaysAssert(!fStencilBuffer->getDeleted()); + fStencilBuffer->unref(); + } + fStencilBuffer = buffer; + if (fStencilBuffer) { + GrAlwaysAssert(!fStencilBuffer->getDeleted()); + fStencilBuffer->ref(); + + GrAlwaysAssert(!fStencilBuffer->getStencilBound(this)); + fStencilBuffer->setStencilBound(this); + } +} diff --git a/src/gpu/gl/debug/GrFrameBufferObj.h b/src/gpu/gl/debug/GrFrameBufferObj.h new file mode 100644 index 0000000000..40e123e49f --- /dev/null +++ b/src/gpu/gl/debug/GrFrameBufferObj.h @@ -0,0 +1,67 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrFrameBufferObj_DEFINED +#define GrFrameBufferObj_DEFINED + +#include "GrFakeRefObj.h" +class GrFBBindableObj; + +//////////////////////////////////////////////////////////////////////////////// +// TODO: when a framebuffer obj is bound the GL_SAMPLES query must return 0 +// TODO: GL_STENCIL_BITS must also be redirected to the framebuffer +class GrFrameBufferObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrFrameBufferObj); + +public: + GrFrameBufferObj() + : GrFakeRefObj() + , fBound(false) + , fColorBuffer(nullptr) + , fDepthBuffer(nullptr) + , fStencilBuffer(nullptr) { + } + + virtual ~GrFrameBufferObj() { + fColorBuffer = nullptr; + fDepthBuffer = nullptr; + fStencilBuffer = nullptr; + } + + void setBound() { fBound = true; } + void resetBound() { fBound = false; } + bool getBound() const { return fBound; } + + void setColor(GrFBBindableObj *buffer); + GrFBBindableObj *getColor() { return fColorBuffer; } + + void setDepth(GrFBBindableObj *buffer); + GrFBBindableObj *getDepth() { return fDepthBuffer; } + + void setStencil(GrFBBindableObj *buffer); + GrFBBindableObj *getStencil() { return fStencilBuffer; } + + void deleteAction() override { + + setColor(nullptr); + setDepth(nullptr); + setStencil(nullptr); + + this->INHERITED::deleteAction(); + } + +protected: +private: + bool fBound; // is this frame buffer currently bound via "glBindFramebuffer"? + GrFBBindableObj * fColorBuffer; + GrFBBindableObj * fDepthBuffer; + GrFBBindableObj * fStencilBuffer; + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrFrameBufferObj_DEFINED diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp new file mode 100644 index 0000000000..02b5cf704d --- /dev/null +++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp @@ -0,0 +1,1236 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "gl/GrGLInterface.h" + +#include "GrBufferObj.h" +#include "GrFrameBufferObj.h" +#include "GrProgramObj.h" +#include "GrRenderBufferObj.h" +#include "GrShaderObj.h" +#include "GrTextureObj.h" +#include "GrTextureUnitObj.h" +#include "GrVertexArrayObj.h" +#include "gl/GrGLTestInterface.h" + +#include "SkMutex.h" + +namespace { + +// Helper macro to make creating an object (where you need to get back a derived type) easier +#define CREATE(className, classEnum) \ + reinterpret_cast<className *>(this->createObj(classEnum)) + +// Helper macro to make creating an object (where you need to get back a derived type) easier +#define FIND(id, className, classEnum) \ + reinterpret_cast<className *>(this->findObject(id, classEnum)) + +class DebugInterface : public GrGLTestInterface { +public: + DebugInterface() + : fCurrGenericID(0) + , fCurrTextureUnit(0) + , fArrayBuffer(nullptr) + , fElementArrayBuffer(nullptr) + , fVertexArray(nullptr) + , fPackRowLength(0) + , fUnpackRowLength(0) + , fPackAlignment(4) + , fFrameBuffer(nullptr) + , fRenderBuffer(nullptr) + , fProgram(nullptr) + , fAbandoned(false) { + for (int i = 0; i < kDefaultMaxTextureUnits; ++i) { + fTextureUnits[i] = + reinterpret_cast<GrTextureUnitObj*>(this->createObj(kTextureUnit_ObjTypes)); + fTextureUnits[i]->ref(); + fTextureUnits[i]->setNumber(i); + } + this->init(kGL_GrGLStandard); + } + + ~DebugInterface() override { + // unref & delete the texture units first so they don't show up on the leak report + for (int i = 0; i < kDefaultMaxTextureUnits; ++i) { + fTextureUnits[i]->unref(); + fTextureUnits[i]->deleteAction(); + } + for (int i = 0; i < fObjects.count(); ++i) { + delete fObjects[i]; + } + fObjects.reset(); + + fArrayBuffer = nullptr; + fElementArrayBuffer = nullptr; + fVertexArray = nullptr; + + this->report(); + } + + void abandon() const override { fAbandoned = true; } + + GrGLvoid activeTexture(GrGLenum texture) override { + // Ganesh offsets the texture unit indices + texture -= GR_GL_TEXTURE0; + GrAlwaysAssert(texture < kDefaultMaxTextureUnits); + fCurrTextureUnit = texture; + } + + GrGLvoid attachShader(GrGLuint programID, GrGLuint shaderID) override { + + GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); + GrAlwaysAssert(program); + + GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes); + GrAlwaysAssert(shader); + + program->AttachShader(shader); + } + + //////////////////////////////////////////////////////////////////////////////// + GrGLvoid bindTexture(GrGLenum target, GrGLuint textureID) override { + GrAlwaysAssert(target == GR_GL_TEXTURE_2D || + target == GR_GL_TEXTURE_RECTANGLE || + target == GR_GL_TEXTURE_EXTERNAL); + + // a textureID of 0 is acceptable - it binds to the default texture target + GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); + + this->setTexture(texture); + } + + //////////////////////////////////////////////////////////////////////////////// + GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, + GrGLenum usage) override { + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || + GR_GL_ELEMENT_ARRAY_BUFFER == target); + GrAlwaysAssert(size >= 0); + GrAlwaysAssert(GR_GL_STREAM_DRAW == usage || + GR_GL_STATIC_DRAW == usage || + GR_GL_DYNAMIC_DRAW == usage); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + default: + SkFAIL("Unexpected target to glBufferData"); + break; + } + + GrAlwaysAssert(buffer); + GrAlwaysAssert(buffer->getBound()); + + buffer->allocate(size, reinterpret_cast<const GrGLchar *>(data)); + buffer->setUsage(usage); + } + + + GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) override { + + switch (pname) { + case GR_GL_UNPACK_ROW_LENGTH: + fUnpackRowLength = param; + break; + case GR_GL_PACK_ROW_LENGTH: + fPackRowLength = param; + break; + case GR_GL_UNPACK_ALIGNMENT: + break; + case GR_GL_PACK_ALIGNMENT: + fPackAlignment = param; + break; + default: + GrAlwaysAssert(false); + break; + } + } + + GrGLvoid readPixels(GrGLint x, + GrGLint y, + GrGLsizei width, + GrGLsizei height, + GrGLenum format, + GrGLenum type, + GrGLvoid* pixels) override { + + GrGLint pixelsInRow = width; + if (fPackRowLength > 0) { + pixelsInRow = fPackRowLength; + } + + GrGLint componentsPerPixel = 0; + + switch (format) { + case GR_GL_RGBA: + // fallthrough + case GR_GL_BGRA: + componentsPerPixel = 4; + break; + case GR_GL_RGB: + componentsPerPixel = 3; + break; + case GR_GL_RED: + componentsPerPixel = 1; + break; + default: + GrAlwaysAssert(false); + break; + } + + GrGLint alignment = fPackAlignment; + + GrGLint componentSize = 0; // size (in bytes) of a single component + + switch (type) { + case GR_GL_UNSIGNED_BYTE: + componentSize = 1; + break; + default: + GrAlwaysAssert(false); + break; + } + + GrGLint rowStride = 0; // number of components (not bytes) to skip + if (componentSize >= alignment) { + rowStride = componentsPerPixel * pixelsInRow; + } else { + float fTemp = + sk_float_ceil(componentSize * componentsPerPixel * pixelsInRow / + static_cast<float>(alignment)); + rowStride = static_cast<GrGLint>(alignment * fTemp / componentSize); + } + + GrGLchar *scanline = static_cast<GrGLchar *>(pixels); + for (int y = 0; y < height; ++y) { + memset(scanline, 0, componentsPerPixel * componentSize * width); + scanline += rowStride; + } + } + + GrGLvoid useProgram(GrGLuint programID) override { + + // A programID of 0 is legal + GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); + + this->useProgram(program); + } + + GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint frameBufferID) override { + + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || + GR_GL_READ_FRAMEBUFFER == target || + GR_GL_DRAW_FRAMEBUFFER); + + // a frameBufferID of 0 is acceptable - it binds to the default + // frame buffer + GrFrameBufferObj *frameBuffer = FIND(frameBufferID, GrFrameBufferObj, + kFrameBuffer_ObjTypes); + + this->setFrameBuffer(frameBuffer); + } + + GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderBufferID) override { + + GrAlwaysAssert(GR_GL_RENDERBUFFER == target); + + // a renderBufferID of 0 is acceptable - it unbinds the bound render buffer + GrRenderBufferObj *renderBuffer = FIND(renderBufferID, GrRenderBufferObj, + kRenderBuffer_ObjTypes); + + this->setRenderBuffer(renderBuffer); + } + + GrGLvoid deleteTextures(GrGLsizei n, const GrGLuint* textures) override { + // first potentially unbind the texture + for (unsigned int i = 0; i < kDefaultMaxTextureUnits; ++i) { + GrTextureUnitObj *pTU = this->getTextureUnit(i); + + if (pTU->getTexture()) { + for (int j = 0; j < n; ++j) { + + if (textures[j] == pTU->getTexture()->getID()) { + // this ID is the current texture - revert the binding to 0 + pTU->setTexture(nullptr); + } + } + } + } + + // TODO: fuse the following block with DeleteRenderBuffers? + // Open GL will remove a deleted render buffer from the active + // frame buffer but not from any other frame buffer + if (this->getFrameBuffer()) { + + GrFrameBufferObj *frameBuffer = this->getFrameBuffer(); + + for (int i = 0; i < n; ++i) { + + if (frameBuffer->getColor() && + textures[i] == frameBuffer->getColor()->getID()) { + frameBuffer->setColor(nullptr); + } + if (frameBuffer->getDepth() && + textures[i] == frameBuffer->getDepth()->getID()) { + frameBuffer->setDepth(nullptr); + } + if (frameBuffer->getStencil() && + textures[i] == frameBuffer->getStencil()->getID()) { + frameBuffer->setStencil(nullptr); + } + } + } + + // then actually "delete" the buffers + for (int i = 0; i < n; ++i) { + GrTextureObj *buffer = FIND(textures[i], GrTextureObj, kTexture_ObjTypes); + GrAlwaysAssert(buffer); + + // OpenGL gives no guarantees if a texture is deleted while attached to + // something other than the currently bound frame buffer + GrAlwaysAssert(!buffer->getBound()); + + GrAlwaysAssert(!buffer->getDeleted()); + buffer->deleteAction(); + } + + } + + GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint *frameBuffers) override { + + // first potentially unbind the buffers + if (this->getFrameBuffer()) { + for (int i = 0; i < n; ++i) { + + if (frameBuffers[i] == + this->getFrameBuffer()->getID()) { + // this ID is the current frame buffer - rebind to the default + this->setFrameBuffer(nullptr); + } + } + } + + // then actually "delete" the buffers + for (int i = 0; i < n; ++i) { + GrFrameBufferObj *buffer = FIND(frameBuffers[i], GrFrameBufferObj, + kFrameBuffer_ObjTypes); + GrAlwaysAssert(buffer); + + GrAlwaysAssert(!buffer->getDeleted()); + buffer->deleteAction(); + } + } + + GrGLvoid deleteRenderbuffers(GrGLsizei n,const GrGLuint *renderBuffers) override { + + // first potentially unbind the buffers + if (this->getRenderBuffer()) { + for (int i = 0; i < n; ++i) { + + if (renderBuffers[i] == + this->getRenderBuffer()->getID()) { + // this ID is the current render buffer - make no + // render buffer be bound + this->setRenderBuffer(nullptr); + } + } + } + + // TODO: fuse the following block with DeleteTextures? + // Open GL will remove a deleted render buffer from the active frame + // buffer but not from any other frame buffer + if (this->getFrameBuffer()) { + + GrFrameBufferObj *frameBuffer = this->getFrameBuffer(); + + for (int i = 0; i < n; ++i) { + + if (frameBuffer->getColor() && + renderBuffers[i] == frameBuffer->getColor()->getID()) { + frameBuffer->setColor(nullptr); + } + if (frameBuffer->getDepth() && + renderBuffers[i] == frameBuffer->getDepth()->getID()) { + frameBuffer->setDepth(nullptr); + } + if (frameBuffer->getStencil() && + renderBuffers[i] == frameBuffer->getStencil()->getID()) { + frameBuffer->setStencil(nullptr); + } + } + } + + // then actually "delete" the buffers + for (int i = 0; i < n; ++i) { + GrRenderBufferObj *buffer = FIND(renderBuffers[i], GrRenderBufferObj, + kRenderBuffer_ObjTypes); + GrAlwaysAssert(buffer); + + // OpenGL gives no guarantees if a render buffer is deleted + // while attached to something other than the currently + // bound frame buffer + GrAlwaysAssert(!buffer->getColorBound()); + GrAlwaysAssert(!buffer->getDepthBound()); + // However, at GrContext destroy time we release all GrRsources and so stencil buffers + // may get deleted before FBOs that refer to them. + //GrAlwaysAssert(!buffer->getStencilBound()); + + GrAlwaysAssert(!buffer->getDeleted()); + buffer->deleteAction(); + } + } + + GrGLvoid framebufferRenderbuffer(GrGLenum target, + GrGLenum attachment, + GrGLenum renderbuffertarget, + GrGLuint renderBufferID) override { + + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); + GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || + GR_GL_DEPTH_ATTACHMENT == attachment || + GR_GL_STENCIL_ATTACHMENT == attachment); + GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget); + + GrFrameBufferObj *framebuffer = this->getFrameBuffer(); + // A render buffer cannot be attached to the default framebuffer + GrAlwaysAssert(framebuffer); + + // a renderBufferID of 0 is acceptable - it unbinds the current + // render buffer + GrRenderBufferObj *renderbuffer = FIND(renderBufferID, GrRenderBufferObj, + kRenderBuffer_ObjTypes); + + switch (attachment) { + case GR_GL_COLOR_ATTACHMENT0: + framebuffer->setColor(renderbuffer); + break; + case GR_GL_DEPTH_ATTACHMENT: + framebuffer->setDepth(renderbuffer); + break; + case GR_GL_STENCIL_ATTACHMENT: + framebuffer->setStencil(renderbuffer); + break; + default: + GrAlwaysAssert(false); + break; + }; + + } + + //////////////////////////////////////////////////////////////////////////////// + GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, + GrGLuint textureID, GrGLint level) override { + + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); + GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || + GR_GL_DEPTH_ATTACHMENT == attachment || + GR_GL_STENCIL_ATTACHMENT == attachment); + GrAlwaysAssert(GR_GL_TEXTURE_2D == textarget); + + GrFrameBufferObj *framebuffer = this->getFrameBuffer(); + // A texture cannot be attached to the default framebuffer + GrAlwaysAssert(framebuffer); + + // A textureID of 0 is allowed - it unbinds the currently bound texture + GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); + if (texture) { + // The texture shouldn't be bound to a texture unit - this + // could lead to a feedback loop + GrAlwaysAssert(!texture->getBound()); + } + + GrAlwaysAssert(0 == level); + + switch (attachment) { + case GR_GL_COLOR_ATTACHMENT0: + framebuffer->setColor(texture); + break; + case GR_GL_DEPTH_ATTACHMENT: + framebuffer->setDepth(texture); + break; + case GR_GL_STENCIL_ATTACHMENT: + framebuffer->setStencil(texture); + break; + default: + GrAlwaysAssert(false); + break; + }; + } + + GrGLuint createProgram() override { + + GrProgramObj *program = CREATE(GrProgramObj, kProgram_ObjTypes); + + return program->getID(); + } + + GrGLuint createShader(GrGLenum type) override { + + GrAlwaysAssert(GR_GL_VERTEX_SHADER == type || + GR_GL_FRAGMENT_SHADER == type); + + GrShaderObj *shader = CREATE(GrShaderObj, kShader_ObjTypes); + shader->setType(type); + + return shader->getID(); + } + + GrGLenum checkFramebufferStatus(GrGLenum target) override { return GR_GL_FRAMEBUFFER_COMPLETE; } + + GrGLvoid deleteProgram(GrGLuint programID) override { + + GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); + GrAlwaysAssert(program); + + if (program->getRefCount()) { + // someone is still using this program so we can't delete it here + program->setMarkedForDeletion(); + } else { + program->deleteAction(); + } + } + + GrGLvoid deleteShader(GrGLuint shaderID) override { + + GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes); + GrAlwaysAssert(shader); + + if (shader->getRefCount()) { + // someone is still using this shader so we can't delete it here + shader->setMarkedForDeletion(); + } else { + shader->deleteAction(); + } + } + + GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override { + this->genObjs(kBuffer_ObjTypes, n, ids); + } + + GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint* ids) override { + this->genObjs(kFrameBuffer_ObjTypes, n, ids); + } + + GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint* ids) override { + this->genObjs(kRenderBuffer_ObjTypes, n, ids); + } + + GrGLvoid genTextures(GrGLsizei n, GrGLuint* ids) override { + this->genObjs(kTexture_ObjTypes, n, ids); + } + + GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint* ids) override { + this->genObjs(kVertexArray_ObjTypes, n, ids); + } + + GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); } + + GrGLenum getError() override { return GR_GL_NO_ERROR; } + + GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) override { + // TODO: remove from Ganesh the #defines for gets we don't use. + // We would like to minimize gets overall due to performance issues + switch (pname) { + case GR_GL_CONTEXT_PROFILE_MASK: + *params = GR_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; + break; + case GR_GL_STENCIL_BITS: + *params = 8; + break; + case GR_GL_SAMPLES: + *params = 1; + break; + case GR_GL_FRAMEBUFFER_BINDING: + *params = 0; + break; + case GR_GL_VIEWPORT: + params[0] = 0; + params[1] = 0; + params[2] = 800; + params[3] = 600; + break; + case GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + *params = 8; + break; + case GR_GL_MAX_TEXTURE_COORDS: + *params = 8; + break; + case GR_GL_MAX_VERTEX_UNIFORM_VECTORS: + *params = kDefaultMaxVertexUniformVectors; + break; + case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS: + *params = kDefaultMaxFragmentUniformVectors; + break; + case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + *params = 16 * 4; + break; + case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS: + *params = 0; + break; + case GR_GL_COMPRESSED_TEXTURE_FORMATS: + break; + case GR_GL_MAX_TEXTURE_SIZE: + *params = 8192; + break; + case GR_GL_MAX_RENDERBUFFER_SIZE: + *params = 8192; + break; + case GR_GL_MAX_SAMPLES: + *params = 32; + break; + case GR_GL_MAX_VERTEX_ATTRIBS: + *params = kDefaultMaxVertexAttribs; + break; + case GR_GL_MAX_VARYING_VECTORS: + *params = kDefaultMaxVaryingVectors; + break; + case GR_GL_NUM_EXTENSIONS: { + GrGLint i = 0; + while (kExtensions[i++]); + *params = i; + break; + } + default: + SkFAIL("Unexpected pname to GetIntegerv"); + } + } + + GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override { + val[0] = val[1] = 0.5f; + } + + GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) override { + this->getShaderOrProgramiv(program, pname, params); + } + + GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) override { + this->getInfoLog(program, bufsize, length, infolog); + } + + GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) override { + switch (pname) { + case GR_GL_CURRENT_QUERY: + *params = 0; + break; + case GR_GL_QUERY_COUNTER_BITS: + *params = 32; + break; + default: + SkFAIL("Unexpected pname passed GetQueryiv."); + } + } + + GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) override { + this->getShaderOrProgramiv(shader, pname, params); + } + + GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) override { + this->getInfoLog(shader, bufsize, length, infolog); + } + + const GrGLubyte* getString(GrGLenum name) override { + switch (name) { + case GR_GL_EXTENSIONS: + return CombinedExtensionString(); + case GR_GL_VERSION: + return (const GrGLubyte*)"4.0 Debug GL"; + case GR_GL_SHADING_LANGUAGE_VERSION: + return (const GrGLubyte*)"4.20.8 Debug GLSL"; + case GR_GL_VENDOR: + return (const GrGLubyte*)"Debug Vendor"; + case GR_GL_RENDERER: + return (const GrGLubyte*)"The Debug (Non-)Renderer"; + default: + SkFAIL("Unexpected name passed to GetString"); + return nullptr; + } + } + + const GrGLubyte* getStringi(GrGLenum name, GrGLuint i) override { + switch (name) { + case GR_GL_EXTENSIONS: { + GrGLint count; + this->getIntegerv(GR_GL_NUM_EXTENSIONS, &count); + if ((GrGLint)i <= count) { + return (const GrGLubyte*) kExtensions[i]; + } else { + return nullptr; + } + } + default: + SkFAIL("Unexpected name passed to GetStringi"); + return nullptr; + } + } + + GrGLvoid getTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, + GrGLint* params) override { + // we used to use this to query stuff about externally created textures, + // now we just require clients to tell us everything about the texture. + SkFAIL("Should never query texture parameters."); + } + + GrGLvoid deleteVertexArrays(GrGLsizei n, const GrGLuint* ids) override { + for (GrGLsizei i = 0; i < n; ++i) { + GrVertexArrayObj* array = FIND(ids[i], GrVertexArrayObj, kVertexArray_ObjTypes); + GrAlwaysAssert(array); + + // Deleting the current vertex array binds object 0 + if (this->getVertexArray() == array) { + this->setVertexArray(nullptr); + } + + if (array->getRefCount()) { + // someone is still using this vertex array so we can't delete it here + array->setMarkedForDeletion(); + } else { + array->deleteAction(); + } + } + } + + GrGLvoid bindVertexArray(GrGLuint id) override { + GrVertexArrayObj* array = FIND(id, GrVertexArrayObj, kVertexArray_ObjTypes); + GrAlwaysAssert((0 == id) || array); + this->setVertexArray(array); + } + + GrGLvoid bindBuffer(GrGLenum target, GrGLuint bufferID) override { + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || GR_GL_ELEMENT_ARRAY_BUFFER == target); + + GrBufferObj *buffer = FIND(bufferID, GrBufferObj, kBuffer_ObjTypes); + // 0 is a permissible bufferID - it unbinds the current buffer + + switch (target) { + case GR_GL_ARRAY_BUFFER: + this->setArrayBuffer(buffer); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + this->setElementArrayBuffer(buffer); + break; + default: + SkFAIL("Unexpected target to glBindBuffer"); + break; + } + } + + // deleting a bound buffer has the side effect of binding 0 + GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override { + // first potentially unbind the buffers + for (int i = 0; i < n; ++i) { + + if (this->getArrayBuffer() && + ids[i] == this->getArrayBuffer()->getID()) { + // this ID is the current array buffer + this->setArrayBuffer(nullptr); + } + if (this->getElementArrayBuffer() && + ids[i] == this->getElementArrayBuffer()->getID()) { + // this ID is the current element array buffer + this->setElementArrayBuffer(nullptr); + } + } + + // then actually "delete" the buffers + for (int i = 0; i < n; ++i) { + GrBufferObj *buffer = FIND(ids[i], GrBufferObj, kBuffer_ObjTypes); + GrAlwaysAssert(buffer); + + GrAlwaysAssert(!buffer->getDeleted()); + buffer->deleteAction(); + } + } + + // map a buffer to the caller's address space + GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, + GrGLbitfield access) override { + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || + GR_GL_ELEMENT_ARRAY_BUFFER == target); + + // We only expect read access and we expect that the buffer or range is always invalidated. + GrAlwaysAssert(!SkToBool(GR_GL_MAP_READ_BIT & access)); + GrAlwaysAssert((GR_GL_MAP_INVALIDATE_BUFFER_BIT | GR_GL_MAP_INVALIDATE_RANGE_BIT) & access); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + default: + SkFAIL("Unexpected target to glMapBufferRange"); + break; + } + + if (buffer) { + GrAlwaysAssert(offset >= 0 && offset + length <= buffer->getSize()); + GrAlwaysAssert(!buffer->getMapped()); + buffer->setMapped(offset, length); + return buffer->getDataPtr() + offset; + } + + GrAlwaysAssert(false); + return nullptr; // no buffer bound to the target + } + + GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override { + GrAlwaysAssert(GR_GL_WRITE_ONLY == access); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + default: + SkFAIL("Unexpected target to glMapBuffer"); + break; + } + + return this->mapBufferRange(target, 0, buffer->getSize(), + GR_GL_MAP_WRITE_BIT | GR_GL_MAP_INVALIDATE_BUFFER_BIT); + } + + // remove a buffer from the caller's address space + // TODO: check if the "access" method from "glMapBuffer" was honored + GrGLboolean unmapBuffer(GrGLenum target) override { + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || + GR_GL_ELEMENT_ARRAY_BUFFER == target); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + default: + SkFAIL("Unexpected target to glUnmapBuffer"); + break; + } + + if (buffer) { + GrAlwaysAssert(buffer->getMapped()); + buffer->resetMapped(); + return GR_GL_TRUE; + } + + GrAlwaysAssert(false); + return GR_GL_FALSE; // GR_GL_INVALID_OPERATION; + } + + GrGLvoid flushMappedBufferRange(GrGLenum target, GrGLintptr offset, + GrGLsizeiptr length) override { + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || + GR_GL_ELEMENT_ARRAY_BUFFER == target); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + default: + SkFAIL("Unexpected target to glUnmapBuffer"); + break; + } + + if (buffer) { + GrAlwaysAssert(buffer->getMapped()); + GrAlwaysAssert(offset >= 0 && (offset + length) <= buffer->getMappedLength()); + } else { + GrAlwaysAssert(false); + } + } + + GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum value, GrGLint* params) override { + + GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || + GR_GL_ELEMENT_ARRAY_BUFFER == target); + GrAlwaysAssert(GR_GL_BUFFER_SIZE == value || + GR_GL_BUFFER_USAGE == value); + + GrBufferObj *buffer = nullptr; + switch (target) { + case GR_GL_ARRAY_BUFFER: + buffer = this->getArrayBuffer(); + break; + case GR_GL_ELEMENT_ARRAY_BUFFER: + buffer = this->getElementArrayBuffer(); + break; + } + + GrAlwaysAssert(buffer); + + switch (value) { + case GR_GL_BUFFER_MAPPED: + *params = GR_GL_FALSE; + if (buffer) + *params = buffer->getMapped() ? GR_GL_TRUE : GR_GL_FALSE; + break; + case GR_GL_BUFFER_SIZE: + *params = 0; + if (buffer) + *params = SkToInt(buffer->getSize()); + break; + case GR_GL_BUFFER_USAGE: + *params = GR_GL_STATIC_DRAW; + if (buffer) + *params = buffer->getUsage(); + break; + default: + SkFAIL("Unexpected value to glGetBufferParamateriv"); + break; + } + } + +private: + // the OpenGLES 2.0 spec says this must be >= 128 + static const GrGLint kDefaultMaxVertexUniformVectors = 128; + + // the OpenGLES 2.0 spec says this must be >=16 + static const GrGLint kDefaultMaxFragmentUniformVectors = 16; + + // the OpenGLES 2.0 spec says this must be >= 8 + static const GrGLint kDefaultMaxVertexAttribs = 8; + + // the OpenGLES 2.0 spec says this must be >= 8 + static const GrGLint kDefaultMaxVaryingVectors = 8; + + // the OpenGLES 2.0 spec says this must be >= 2 + static const GrGLint kDefaultMaxTextureUnits = 8; + + static const char* kExtensions[]; + + GrGLuint fCurrGenericID; + GrGLuint fCurrTextureUnit; + GrTextureUnitObj* fTextureUnits[kDefaultMaxTextureUnits]; + GrBufferObj* fArrayBuffer; + GrBufferObj* fElementArrayBuffer; + GrVertexArrayObj* fVertexArray; + GrGLint fPackRowLength; + GrGLint fUnpackRowLength; + GrGLint fPackAlignment; + GrFrameBufferObj* fFrameBuffer; + GrRenderBufferObj* fRenderBuffer; + GrProgramObj* fProgram; + mutable bool fAbandoned; + // global store of all objects + SkTArray<GrFakeRefObj *> fObjects; + + static const GrGLubyte* CombinedExtensionString() { + static SkString gExtString; + static SkMutex gMutex; + gMutex.acquire(); + if (0 == gExtString.size()) { + int i = 0; + while (kExtensions[i]) { + if (i > 0) { + gExtString.append(" "); + } + gExtString.append(kExtensions[i]); + ++i; + } + } + gMutex.release(); + return (const GrGLubyte*) gExtString.c_str(); + } + + GrGLvoid genGenericIds(GrGLsizei n, GrGLuint* ids) { + for (int i = 0; i < n; ++i) { + ids[i] = ++fCurrGenericID; + } + } + + GrGLvoid getInfoLog(GrGLuint object, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) { + if (length) { + *length = 0; + } + if (bufsize > 0) { + *infolog = 0; + } + } + + GrGLvoid getShaderOrProgramiv(GrGLuint object, GrGLenum pname, GrGLint* params) { + switch (pname) { + case GR_GL_LINK_STATUS: // fallthru + case GR_GL_COMPILE_STATUS: + *params = GR_GL_TRUE; + break; + case GR_GL_INFO_LOG_LENGTH: + *params = 0; + break; + // we don't expect any other pnames + default: + SkFAIL("Unexpected pname to GetProgramiv"); + break; + } + } + + template <typename T> + void queryResult(GrGLenum GLtarget, GrGLenum pname, T *params) { + switch (pname) { + case GR_GL_QUERY_RESULT_AVAILABLE: + *params = GR_GL_TRUE; + break; + case GR_GL_QUERY_RESULT: + *params = 0; + break; + default: + SkFAIL("Unexpected pname passed to GetQueryObject."); + break; + } + } + + enum ObjTypes { + kTexture_ObjTypes = 0, + kBuffer_ObjTypes, + kRenderBuffer_ObjTypes, + kFrameBuffer_ObjTypes, + kShader_ObjTypes, + kProgram_ObjTypes, + kTextureUnit_ObjTypes, + kVertexArray_ObjTypes, + kObjTypeCount + }; + + typedef GrFakeRefObj *(*Create)(); + + static Create gFactoryFunc[kObjTypeCount]; + + GrGLvoid genObjs(ObjTypes type, GrGLsizei n, GrGLuint* ids) { + for (int i = 0; i < n; ++i) { + GrAlwaysAssert(ids[i] == 0); + GrFakeRefObj *obj = this->createObj(type); + GrAlwaysAssert(obj); + ids[i] = obj->getID(); + } + } + + GrFakeRefObj* createObj(ObjTypes type) { + GrFakeRefObj *temp = (*gFactoryFunc[type])(); + + fObjects.push_back(temp); + + return temp; + } + + GrFakeRefObj* findObject(GrGLuint ID, ObjTypes type) { + for (int i = 0; i < fObjects.count(); ++i) { + if (fObjects[i]->getID() == ID) { // && fObjects[i]->getType() == type) { + // The application shouldn't be accessing objects + // that (as far as OpenGL knows) were already deleted + GrAlwaysAssert(!fObjects[i]->getDeleted()); + GrAlwaysAssert(!fObjects[i]->getMarkedForDeletion()); + return fObjects[i]; + } + } + return nullptr; + } + + GrTextureUnitObj* getTextureUnit(int unit) { + GrAlwaysAssert(0 <= unit && kDefaultMaxTextureUnits > unit); + + return fTextureUnits[unit]; + } + + void setArrayBuffer(GrBufferObj *arrayBuffer) { + if (fArrayBuffer) { + // automatically break the binding of the old buffer + GrAlwaysAssert(fArrayBuffer->getBound()); + fArrayBuffer->resetBound(); + + GrAlwaysAssert(!fArrayBuffer->getDeleted()); + fArrayBuffer->unref(); + } + + fArrayBuffer = arrayBuffer; + + if (fArrayBuffer) { + GrAlwaysAssert(!fArrayBuffer->getDeleted()); + fArrayBuffer->ref(); + + GrAlwaysAssert(!fArrayBuffer->getBound()); + fArrayBuffer->setBound(); + } + } + + GrBufferObj* getArrayBuffer() { return fArrayBuffer; } + void setElementArrayBuffer(GrBufferObj *elementArrayBuffer) { + if (fElementArrayBuffer) { + // automatically break the binding of the old buffer + GrAlwaysAssert(fElementArrayBuffer->getBound()); + fElementArrayBuffer->resetBound(); + + GrAlwaysAssert(!fElementArrayBuffer->getDeleted()); + fElementArrayBuffer->unref(); + } + + fElementArrayBuffer = elementArrayBuffer; + + if (fElementArrayBuffer) { + GrAlwaysAssert(!fElementArrayBuffer->getDeleted()); + fElementArrayBuffer->ref(); + + GrAlwaysAssert(!fElementArrayBuffer->getBound()); + fElementArrayBuffer->setBound(); + } + } + + GrBufferObj *getElementArrayBuffer() { return fElementArrayBuffer; } + + void setVertexArray(GrVertexArrayObj* vertexArray) { + if (vertexArray) { + SkASSERT(!vertexArray->getDeleted()); + } + SkRefCnt_SafeAssign(fVertexArray, vertexArray); + } + + GrVertexArrayObj* getVertexArray() { return fVertexArray; } + + void setTexture(GrTextureObj *texture) { + fTextureUnits[fCurrTextureUnit]->setTexture(texture); + } + + void setFrameBuffer(GrFrameBufferObj *frameBuffer) { + if (fFrameBuffer) { + GrAlwaysAssert(fFrameBuffer->getBound()); + fFrameBuffer->resetBound(); + + GrAlwaysAssert(!fFrameBuffer->getDeleted()); + fFrameBuffer->unref(); + } + + fFrameBuffer = frameBuffer; + + if (fFrameBuffer) { + GrAlwaysAssert(!fFrameBuffer->getDeleted()); + fFrameBuffer->ref(); + + GrAlwaysAssert(!fFrameBuffer->getBound()); + fFrameBuffer->setBound(); + } + } + + GrFrameBufferObj *getFrameBuffer() { return fFrameBuffer; } + + void setRenderBuffer(GrRenderBufferObj *renderBuffer) { + if (fRenderBuffer) { + GrAlwaysAssert(fRenderBuffer->getBound()); + fRenderBuffer->resetBound(); + + GrAlwaysAssert(!fRenderBuffer->getDeleted()); + fRenderBuffer->unref(); + } + + fRenderBuffer = renderBuffer; + + if (fRenderBuffer) { + GrAlwaysAssert(!fRenderBuffer->getDeleted()); + fRenderBuffer->ref(); + + GrAlwaysAssert(!fRenderBuffer->getBound()); + fRenderBuffer->setBound(); + } + } + GrRenderBufferObj *getRenderBuffer() { return fRenderBuffer; } + + void useProgram(GrProgramObj *program) { + if (fProgram) { + GrAlwaysAssert(fProgram->getInUse()); + fProgram->resetInUse(); + + GrAlwaysAssert(!fProgram->getDeleted()); + fProgram->unref(); + } + + fProgram = program; + + if (fProgram) { + GrAlwaysAssert(!fProgram->getDeleted()); + fProgram->ref(); + + GrAlwaysAssert(!fProgram->getInUse()); + fProgram->setInUse(); + } + } + + void report() const { + for (int i = 0; i < fObjects.count(); ++i) { + if (!fAbandoned) { + GrAlwaysAssert(0 == fObjects[i]->getRefCount()); + GrAlwaysAssert(fObjects[i]->getDeleted()); + } + } + } + + typedef GrGLTestInterface INHERITED; +}; + +#undef CREATE +#undef FIND + +DebugInterface::Create DebugInterface::gFactoryFunc[kObjTypeCount] = { + GrTextureObj::createGrTextureObj, + GrBufferObj::createGrBufferObj, + GrRenderBufferObj::createGrRenderBufferObj, + GrFrameBufferObj::createGrFrameBufferObj, + GrShaderObj::createGrShaderObj, + GrProgramObj::createGrProgramObj, + GrTextureUnitObj::createGrTextureUnitObj, + GrVertexArrayObj::createGrVertexArrayObj, +}; + +const char* DebugInterface::kExtensions[] = { + "GL_ARB_framebuffer_object", + "GL_ARB_blend_func_extended", + "GL_ARB_timer_query", + "GL_ARB_draw_buffers", + "GL_ARB_occlusion_query", + "GL_EXT_stencil_wrap", + nullptr, // signifies the end of the array. +}; + +} // anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// + +const GrGLInterface* GrGLCreateDebugInterface() { return new DebugInterface; } diff --git a/src/gpu/gl/debug/GrProgramObj.cpp b/src/gpu/gl/debug/GrProgramObj.cpp new file mode 100644 index 0000000000..d45bd1c113 --- /dev/null +++ b/src/gpu/gl/debug/GrProgramObj.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrProgramObj.h" +#include "GrShaderObj.h" + +void GrProgramObj::AttachShader(GrShaderObj *shader) { + shader->ref(); + fShaders.push_back(shader); +} + +void GrProgramObj::deleteAction() { + + // shaders are automatically detached from a deleted program. They will only be + // deleted if they were marked for deletion by a prior call to glDeleteShader + for (int i = 0; i < fShaders.count(); ++i) { + fShaders[i]->unref(); + } + fShaders.reset(); + + this->INHERITED::deleteAction(); +} diff --git a/src/gpu/gl/debug/GrProgramObj.h b/src/gpu/gl/debug/GrProgramObj.h new file mode 100644 index 0000000000..6e5ec65d9d --- /dev/null +++ b/src/gpu/gl/debug/GrProgramObj.h @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrProgramObj_DEFINED +#define GrProgramObj_DEFINED + +#include "SkTArray.h" +#include "GrFakeRefObj.h" +class GrShaderObj; + +//////////////////////////////////////////////////////////////////////////////// +class GrProgramObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrProgramObj); + +public: + GrProgramObj() + : GrFakeRefObj() + , fInUse(false) {} + + void AttachShader(GrShaderObj *shader); + + void deleteAction() override; + + // TODO: this flag system won't work w/ multiple contexts! + void setInUse() { fInUse = true; } + void resetInUse() { fInUse = false; } + bool getInUse() const { return fInUse; } + +protected: + +private: + SkTArray<GrShaderObj *> fShaders; + bool fInUse; // has this program been activated by a glUseProgram call? + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrProgramObj_DEFINED diff --git a/src/gpu/gl/debug/GrRenderBufferObj.h b/src/gpu/gl/debug/GrRenderBufferObj.h new file mode 100644 index 0000000000..dae08f5c78 --- /dev/null +++ b/src/gpu/gl/debug/GrRenderBufferObj.h @@ -0,0 +1,39 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrRenderBufferObj_DEFINED +#define GrRenderBufferObj_DEFINED + +#include "GrFBBindableObj.h" + +//////////////////////////////////////////////////////////////////////////////// +class GrRenderBufferObj : public GrFBBindableObj { + GR_DEFINE_CREATOR(GrRenderBufferObj); + +public: + GrRenderBufferObj() + : GrFBBindableObj() + , fBound(false) { + } + + void setBound() { fBound = true; } + void resetBound() { fBound = false; } + bool getBound() const { return fBound; } + + void deleteAction() override { + + this->INHERITED::deleteAction(); + } + +protected: +private: + bool fBound; // is this render buffer currently bound via "glBindRenderbuffer"? + + typedef GrFBBindableObj INHERITED; +}; + +#endif // GrRenderBufferObj_DEFINED diff --git a/src/gpu/gl/debug/GrShaderObj.cpp b/src/gpu/gl/debug/GrShaderObj.cpp new file mode 100644 index 0000000000..67cca3ed72 --- /dev/null +++ b/src/gpu/gl/debug/GrShaderObj.cpp @@ -0,0 +1,13 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrShaderObj.h" + +void GrShaderObj::deleteAction() { + + this->INHERITED::deleteAction(); +} diff --git a/src/gpu/gl/debug/GrShaderObj.h b/src/gpu/gl/debug/GrShaderObj.h new file mode 100644 index 0000000000..871494ac8d --- /dev/null +++ b/src/gpu/gl/debug/GrShaderObj.h @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrShaderObj_DEFINED +#define GrShaderObj_DEFINED + +#include "GrFakeRefObj.h" +#include "../GrGLDefines.h" + +//////////////////////////////////////////////////////////////////////////////// +class GrShaderObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrShaderObj); + +public: + GrShaderObj() + : GrFakeRefObj() + , fType(GR_GL_VERTEX_SHADER) {} + + void setType(GrGLenum type) { fType = type; } + GrGLenum getType() { return fType; } + + void deleteAction() override; + +protected: +private: + GrGLenum fType; // either GR_GL_VERTEX_SHADER or GR_GL_FRAGMENT_SHADER + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrShaderObj_DEFINED diff --git a/src/gpu/gl/debug/GrTextureObj.cpp b/src/gpu/gl/debug/GrTextureObj.cpp new file mode 100644 index 0000000000..62cb399245 --- /dev/null +++ b/src/gpu/gl/debug/GrTextureObj.cpp @@ -0,0 +1,13 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureObj.h" + +void GrTextureObj::deleteAction() { + + this->INHERITED::deleteAction(); +} diff --git a/src/gpu/gl/debug/GrTextureObj.h b/src/gpu/gl/debug/GrTextureObj.h new file mode 100644 index 0000000000..bc649f3b38 --- /dev/null +++ b/src/gpu/gl/debug/GrTextureObj.h @@ -0,0 +1,56 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureObj_DEFINED +#define GrTextureObj_DEFINED + +#include "GrFBBindableObj.h" + +class GrTextureUnitObj; + +//////////////////////////////////////////////////////////////////////////////// +class GrTextureObj : public GrFBBindableObj { + GR_DEFINE_CREATOR(GrTextureObj); + +public: + GrTextureObj() + : GrFBBindableObj() { + } + + virtual ~GrTextureObj() { + GrAlwaysAssert(0 == fTextureUnitReferees.count()); + } + + void setBound(GrTextureUnitObj *referee) { + fTextureUnitReferees.append(1, &referee); + } + + void resetBound(GrTextureUnitObj *referee) { + int index = fTextureUnitReferees.find(referee); + GrAlwaysAssert(0 <= index); + fTextureUnitReferees.removeShuffle(index); + } + bool getBound(GrTextureUnitObj *referee) const { + int index = fTextureUnitReferees.find(referee); + return 0 <= index; + } + bool getBound() const { + return 0 != fTextureUnitReferees.count(); + } + + void deleteAction() override; + +protected: + +private: + // texture units that bind this texture (via "glBindTexture") + SkTDArray<GrTextureUnitObj *> fTextureUnitReferees; + + typedef GrFBBindableObj INHERITED; +}; + +#endif // GrTextureObj_DEFINED diff --git a/src/gpu/gl/debug/GrTextureUnitObj.cpp b/src/gpu/gl/debug/GrTextureUnitObj.cpp new file mode 100644 index 0000000000..8de0b092cd --- /dev/null +++ b/src/gpu/gl/debug/GrTextureUnitObj.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureUnitObj.h" +#include "GrTextureObj.h" + +void GrTextureUnitObj::setTexture(GrTextureObj *texture) { + + if (fTexture) { + GrAlwaysAssert(fTexture->getBound(this)); + fTexture->resetBound(this); + + GrAlwaysAssert(!fTexture->getDeleted()); + fTexture->unref(); + } + + fTexture = texture; + + if (fTexture) { + GrAlwaysAssert(!fTexture->getDeleted()); + fTexture->ref(); + + GrAlwaysAssert(!fTexture->getBound(this)); + fTexture->setBound(this); + } +} diff --git a/src/gpu/gl/debug/GrTextureUnitObj.h b/src/gpu/gl/debug/GrTextureUnitObj.h new file mode 100644 index 0000000000..5c7a03980b --- /dev/null +++ b/src/gpu/gl/debug/GrTextureUnitObj.h @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureUnitObj_DEFINED +#define GrTextureUnitObj_DEFINED + +#include "GrFakeRefObj.h" +class GrTextureObj; + +//////////////////////////////////////////////////////////////////////////////// +// Although texture unit objects are allocated & deallocated like the other +// GL emulation objects they are derived from GrFakeRefObj to provide some +// uniformity in how the debug interface class manages resources +class GrTextureUnitObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrTextureUnitObj); + +public: + GrTextureUnitObj() + : GrFakeRefObj() + , fNumber(0) + , fTexture(nullptr) { + } + + void setNumber(GrGLenum number) { + fNumber = number; + } + GrGLenum getNumber() const { return fNumber; } + + void setTexture(GrTextureObj *texture); + GrTextureObj *getTexture() { return fTexture; } + +protected: +private: + GrGLenum fNumber; + GrTextureObj *fTexture; + + typedef GrFakeRefObj INHERITED; +}; + +#endif // GrTextureUnitObj_DEFINED diff --git a/src/gpu/gl/debug/GrVertexArrayObj.h b/src/gpu/gl/debug/GrVertexArrayObj.h new file mode 100644 index 0000000000..989c610924 --- /dev/null +++ b/src/gpu/gl/debug/GrVertexArrayObj.h @@ -0,0 +1,21 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVertexArrayObj_DEFINED +#define GrVertexArrayObj_DEFINED + +#include "GrFakeRefObj.h" + +class GrVertexArrayObj : public GrFakeRefObj { + GR_DEFINE_CREATOR(GrVertexArrayObj); + +public: + GrVertexArrayObj() : GrFakeRefObj() {} + + typedef GrFakeRefObj INHERITED; +}; +#endif diff --git a/src/gpu/gl/debug/SkDebugGLContext.cpp b/src/gpu/gl/debug/SkDebugGLContext.cpp new file mode 100644 index 0000000000..ecb27db95d --- /dev/null +++ b/src/gpu/gl/debug/SkDebugGLContext.cpp @@ -0,0 +1,16 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/debug/SkDebugGLContext.h" + +SkDebugGLContext::SkDebugGLContext() { + this->init(GrGLCreateDebugInterface()); +} + +SkDebugGLContext::~SkDebugGLContext() { + this->teardown(); +} diff --git a/src/gpu/gl/debug/SkDebugGLContext.h b/src/gpu/gl/debug/SkDebugGLContext.h new file mode 100644 index 0000000000..1ea7b62931 --- /dev/null +++ b/src/gpu/gl/debug/SkDebugGLContext.h @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkDebugGLContext_DEFINED +#define SkDebugGLContext_DEFINED + +#include "gl/SkGLContext.h" + +class SkDebugGLContext : public SkGLContext { +public: + ~SkDebugGLContext() override; + + static SkDebugGLContext* Create() { + return new SkDebugGLContext; + } +private: + void onPlatformMakeCurrent() const override {} + void onPlatformSwapBuffers() const override {} + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } + + SkDebugGLContext(); +}; + +#endif diff --git a/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp new file mode 100644 index 0000000000..1644254c09 --- /dev/null +++ b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp @@ -0,0 +1,333 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "gl/SkGLContext.h" + +#include <GLES2/gl2.h> + +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "gl/GrGLDefines.h" +#include "gl/GrGLUtil.h" + +namespace { + +// TODO: Share this class with ANGLE if/when it gets support for EGL_KHR_fence_sync. +class SkEGLFenceSync : public SkGpuFenceSync { +public: + static SkEGLFenceSync* CreateIfSupported(EGLDisplay); + + SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const override; + bool waitFence(SkPlatformGpuFence fence, bool flush) const override; + void deleteFence(SkPlatformGpuFence fence) const override; + +private: + SkEGLFenceSync(EGLDisplay display) : fDisplay(display) {} + + EGLDisplay fDisplay; + + typedef SkGpuFenceSync INHERITED; +}; + +class EGLGLContext : public SkGLContext { +public: + EGLGLContext(GrGLStandard forcedGpuAPI); + ~EGLGLContext() override; + + GrEGLImage texture2DToEGLImage(GrGLuint texID) const override; + void destroyEGLImage(GrEGLImage) const override; + GrGLuint eglImageToExternalTexture(GrEGLImage) const override; + SkGLContext* createNew() const override; + +private: + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; + + EGLContext fContext; + EGLDisplay fDisplay; + EGLSurface fSurface; +}; + +EGLGLContext::EGLGLContext(GrGLStandard forcedGpuAPI) + : fContext(EGL_NO_CONTEXT) + , fDisplay(EGL_NO_DISPLAY) + , fSurface(EGL_NO_SURFACE) { + static const EGLint kEGLContextAttribsForOpenGL[] = { + EGL_NONE + }; + + static const EGLint kEGLContextAttribsForOpenGLES[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + static const struct { + const EGLint* fContextAttribs; + EGLenum fAPI; + EGLint fRenderableTypeBit; + GrGLStandard fStandard; + } kAPIs[] = { + { // OpenGL + kEGLContextAttribsForOpenGL, + EGL_OPENGL_API, + EGL_OPENGL_BIT, + kGL_GrGLStandard + }, + { // OpenGL ES. This seems to work for both ES2 and 3 (when available). + kEGLContextAttribsForOpenGLES, + EGL_OPENGL_ES_API, + EGL_OPENGL_ES2_BIT, + kGLES_GrGLStandard + }, + }; + + size_t apiLimit = SK_ARRAY_COUNT(kAPIs); + size_t api = 0; + if (forcedGpuAPI == kGL_GrGLStandard) { + apiLimit = 1; + } else if (forcedGpuAPI == kGLES_GrGLStandard) { + api = 1; + } + SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kAPIs[api].fStandard == forcedGpuAPI); + + SkAutoTUnref<const GrGLInterface> gl; + + for (; nullptr == gl.get() && api < apiLimit; ++api) { + fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + EGLint majorVersion; + EGLint minorVersion; + eglInitialize(fDisplay, &majorVersion, &minorVersion); + +#if 0 + SkDebugf("VENDOR: %s\n", eglQueryString(fDisplay, EGL_VENDOR)); + SkDebugf("APIS: %s\n", eglQueryString(fDisplay, EGL_CLIENT_APIS)); + SkDebugf("VERSION: %s\n", eglQueryString(fDisplay, EGL_VERSION)); + SkDebugf("EXTENSIONS %s\n", eglQueryString(fDisplay, EGL_EXTENSIONS)); +#endif + + if (!eglBindAPI(kAPIs[api].fAPI)) { + continue; + } + + EGLint numConfigs = 0; + const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, kAPIs[api].fRenderableTypeBit, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + + EGLConfig surfaceConfig; + if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) { + SkDebugf("eglChooseConfig failed. EGL Error: 0x%08x\n", eglGetError()); + continue; + } + + if (0 == numConfigs) { + SkDebugf("No suitable EGL config found.\n"); + continue; + } + + fContext = eglCreateContext(fDisplay, surfaceConfig, nullptr, kAPIs[api].fContextAttribs); + if (EGL_NO_CONTEXT == fContext) { + SkDebugf("eglCreateContext failed. EGL Error: 0x%08x\n", eglGetError()); + continue; + } + + static const EGLint kSurfaceAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + + fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, kSurfaceAttribs); + if (EGL_NO_SURFACE == fSurface) { + SkDebugf("eglCreatePbufferSurface failed. EGL Error: 0x%08x\n", eglGetError()); + this->destroyGLContext(); + continue; + } + + if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("eglMakeCurrent failed. EGL Error: 0x%08x\n", eglGetError()); + this->destroyGLContext(); + continue; + } + + gl.reset(GrGLCreateNativeInterface()); + if (nullptr == gl.get()) { + SkDebugf("Failed to create gl interface.\n"); + this->destroyGLContext(); + continue; + } + + if (!gl->validate()) { + SkDebugf("Failed to validate gl interface.\n"); + this->destroyGLContext(); + continue; + } + + this->init(gl.release(), SkEGLFenceSync::CreateIfSupported(fDisplay)); + break; + } +} + +EGLGLContext::~EGLGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void EGLGLContext::destroyGLContext() { + if (fDisplay) { + eglMakeCurrent(fDisplay, 0, 0, 0); + + if (fContext) { + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + } + + if (fSurface) { + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + } + + //TODO should we close the display? + fDisplay = EGL_NO_DISPLAY; + } +} + +GrEGLImage EGLGLContext::texture2DToEGLImage(GrGLuint texID) const { + if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) { + return GR_EGL_NO_IMAGE; + } + GrEGLImage img; + GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE }; + GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID); + GR_GL_CALL_RET(this->gl(), img, + EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs)); + return img; +} + +void EGLGLContext::destroyEGLImage(GrEGLImage image) const { + GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image)); +} + +GrGLuint EGLGLContext::eglImageToExternalTexture(GrEGLImage image) const { + GrGLClearErr(this->gl()); + if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) { + return 0; + } + typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage); + + EGLImageTargetTexture2DProc glEGLImageTargetTexture2D = + (EGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (!glEGLImageTargetTexture2D) { + return 0; + } + GrGLuint texID; + glGenTextures(1, &texID); + if (!texID) { + return 0; + } + glBindTexture(GR_GL_TEXTURE_EXTERNAL, texID); + if (glGetError() != GR_GL_NO_ERROR) { + glDeleteTextures(1, &texID); + return 0; + } + glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image); + if (glGetError() != GR_GL_NO_ERROR) { + glDeleteTextures(1, &texID); + return 0; + } + return texID; +} + +SkGLContext* EGLGLContext::createNew() const { + SkGLContext* ctx = SkCreatePlatformGLContext(this->gl()->fStandard); + if (ctx) { + ctx->makeCurrent(); + } + return ctx; +} + +void EGLGLContext::onPlatformMakeCurrent() const { + if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Could not set the context.\n"); + } +} + +void EGLGLContext::onPlatformSwapBuffers() const { + if (!eglSwapBuffers(fDisplay, fSurface)) { + SkDebugf("Could not complete eglSwapBuffers.\n"); + } +} + +GrGLFuncPtr EGLGLContext::onPlatformGetProcAddress(const char* procName) const { + return eglGetProcAddress(procName); +} + +static bool supports_egl_extension(EGLDisplay display, const char* extension) { + size_t extensionLength = strlen(extension); + const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS); + while (const char* match = strstr(extensionsStr, extension)) { + // Ensure the string we found is its own extension, not a substring of a larger extension + // (e.g. GL_ARB_occlusion_query / GL_ARB_occlusion_query2). + if ((match == extensionsStr || match[-1] == ' ') && + (match[extensionLength] == ' ' || match[extensionLength] == '\0')) { + return true; + } + extensionsStr = match + extensionLength; + } + return false; +} + +SkEGLFenceSync* SkEGLFenceSync::CreateIfSupported(EGLDisplay display) { + if (!display || !supports_egl_extension(display, "EGL_KHR_fence_sync")) { + return nullptr; + } + return new SkEGLFenceSync(display); +} + +SkPlatformGpuFence SkEGLFenceSync::insertFence() const { + return eglCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr); +} + +bool SkEGLFenceSync::waitFence(SkPlatformGpuFence platformFence, bool flush) const { + EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence); + return EGL_CONDITION_SATISFIED_KHR == + eglClientWaitSyncKHR(fDisplay, + eglsync, + flush ? EGL_SYNC_FLUSH_COMMANDS_BIT_KHR : 0, + EGL_FOREVER_KHR); +} + +void SkEGLFenceSync::deleteFence(SkPlatformGpuFence platformFence) const { + EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence); + eglDestroySyncKHR(fDisplay, eglsync); +} + +} // anonymous namespace + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + SkASSERT(!shareContext); + if (shareContext) { + return nullptr; + } + EGLGLContext* ctx = new EGLGLContext(forcedGpuAPI); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} diff --git a/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp b/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp new file mode 100644 index 0000000000..d0bd1de428 --- /dev/null +++ b/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp @@ -0,0 +1,343 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "gl/SkGLContext.h" + +#include <X11/Xlib.h> +#include <GL/glx.h> +#include <GL/glu.h> + +namespace { + +/* Note: Skia requires glx 1.3 or newer */ + +/* This struct is taken from a mesa demo. Please update as required */ +static const struct { int major, minor; } gl_versions[] = { + {1, 0}, + {1, 1}, + {1, 2}, + {1, 3}, + {1, 4}, + {1, 5}, + {2, 0}, + {2, 1}, + {3, 0}, + {3, 1}, + {3, 2}, + {3, 3}, + {4, 0}, + {4, 1}, + {4, 2}, + {4, 3}, + {4, 4}, + {0, 0} /* end of list */ +}; +#define NUM_GL_VERSIONS SK_ARRAY_COUNT(gl_versions) + +static bool ctxErrorOccurred = false; +static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { + ctxErrorOccurred = true; + return 0; +} + +class GLXGLContext : public SkGLContext { +public: + GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareList); + ~GLXGLContext() override; + +private: + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; + + GLXContext fContext; + Display* fDisplay; + Pixmap fPixmap; + GLXPixmap fGlxPixmap; +}; + +GLXGLContext::GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareContext) + : fContext(nullptr) + , fDisplay(nullptr) + , fPixmap(0) + , fGlxPixmap(0) { + fDisplay = XOpenDisplay(0); + + GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr; + + if (!fDisplay) { + SkDebugf("Failed to open X display.\n"); + this->destroyGLContext(); + return; + } + + // Get a matching FB config + static int visual_attribs[] = { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT, + None + }; + + int glx_major, glx_minor; + + // FBConfigs were added in GLX version 1.3. + if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) || + ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) { + SkDebugf("GLX version 1.3 or higher required.\n"); + this->destroyGLContext(); + return; + } + + //SkDebugf("Getting matching framebuffer configs.\n"); + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), + visual_attribs, &fbcount); + if (!fbc) { + SkDebugf("Failed to retrieve a framebuffer config.\n"); + this->destroyGLContext(); + return; + } + //SkDebugf("Found %d matching FB configs.\n", fbcount); + + // Pick the FB config/visual with the most samples per pixel + //SkDebugf("Getting XVisualInfos.\n"); + int best_fbc = -1, best_num_samp = -1; + + int i; + for (i = 0; i < fbcount; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]); + if (vi) { + int samp_buf, samples; + glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples); + + //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d," + // " SAMPLES = %d\n", + // i, (unsigned int)vi->visualid, samp_buf, samples); + + if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) + best_fbc = i, best_num_samp = samples; + } + XFree(vi); + } + + GLXFBConfig bestFbc = fbc[best_fbc]; + + // Be sure to free the FBConfig list allocated by glXChooseFBConfig() + XFree(fbc); + + // Get a visual + XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc); + //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid); + + fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth); + + if (!fPixmap) { + SkDebugf("Failed to create pixmap.\n"); + this->destroyGLContext(); + return; + } + + fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap); + + // Done with the visual info data + XFree(vi); + + // Create the context + + // Install an X error handler so the application won't exit if GL 3.0 + // context allocation fails. + // + // Note this error handler is global. + // All display connections in all threads of a process use the same + // error handler, so be sure to guard against other threads issuing + // X commands while this code is running. + ctxErrorOccurred = false; + int (*oldHandler)(Display*, XErrorEvent*) = + XSetErrorHandler(&ctxErrorHandler); + + // Get the default screen's GLX extension list + const char *glxExts = glXQueryExtensionsString( + fDisplay, DefaultScreen(fDisplay) + ); + + + // Check for the GLX_ARB_create_context extension string and the function. + // If either is not present, use GLX 1.3 context creation method. + if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"), + reinterpret_cast<const GLubyte*>(glxExts))) { + if (kGLES_GrGLStandard != forcedGpuAPI) { + fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True); + } + } else { + //SkDebugf("Creating context.\n"); + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = + (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB"); + + if (kGLES_GrGLStandard == forcedGpuAPI) { + if (gluCheckExtension( + reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"), + reinterpret_cast<const GLubyte*>(glxExts))) { + static const int context_attribs_gles[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, + None + }; + fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, + context_attribs_gles); + } + } else { + // Well, unfortunately GLX will not just give us the highest context so instead we have + // to do this nastiness + for (i = NUM_GL_VERSIONS - 2; i > 0 ; i--) { + /* don't bother below GL 3.0 */ + if (gl_versions[i].major == 3 && gl_versions[i].minor == 0) { + break; + } + // On Nvidia GPUs, to use Nv Path rendering we need a compatibility profile for the + // time being. + // TODO when Nvidia implements NVPR on Core profiles, we should start requesting + // core here + static const int context_attribs_gl[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, gl_versions[i].major, + GLX_CONTEXT_MINOR_VERSION_ARB, gl_versions[i].minor, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + None + }; + fContext = + glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, + context_attribs_gl); + + // Sync to ensure any errors generated are processed. + XSync(fDisplay, False); + + if (!ctxErrorOccurred && fContext) { + break; + } + // try again + ctxErrorOccurred = false; + } + + // Couldn't create GL 3.0 context. + // Fall back to old-style 2.x context. + // When a context version below 3.0 is requested, + // implementations will return the newest context version + // compatible with OpenGL versions less than version 3.0. + if (ctxErrorOccurred || !fContext) { + static const int context_attribs_gl_fallback[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 1, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + + ctxErrorOccurred = false; + + fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, + context_attribs_gl_fallback); + } + } + } + + // Sync to ensure any errors generated are processed. + XSync(fDisplay, False); + + // Restore the original error handler + XSetErrorHandler(oldHandler); + + if (ctxErrorOccurred || !fContext) { + SkDebugf("Failed to create an OpenGL context.\n"); + this->destroyGLContext(); + return; + } + + // Verify that context is a direct context + if (!glXIsDirect(fDisplay, fContext)) { + //SkDebugf("Indirect GLX rendering context obtained.\n"); + } else { + //SkDebugf("Direct GLX rendering context obtained.\n"); + } + + //SkDebugf("Making context current.\n"); + if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { + SkDebugf("Could not set the context.\n"); + this->destroyGLContext(); + return; + } + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface()); + if (nullptr == gl.get()) { + SkDebugf("Failed to create gl interface"); + this->destroyGLContext(); + return; + } + + if (!gl->validate()) { + SkDebugf("Failed to validate gl interface"); + this->destroyGLContext(); + return; + } + + this->init(gl.release()); +} + + +GLXGLContext::~GLXGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void GLXGLContext::destroyGLContext() { + if (fDisplay) { + glXMakeCurrent(fDisplay, 0, 0); + + if (fContext) { + glXDestroyContext(fDisplay, fContext); + fContext = nullptr; + } + + if (fGlxPixmap) { + glXDestroyGLXPixmap(fDisplay, fGlxPixmap); + fGlxPixmap = 0; + } + + if (fPixmap) { + XFreePixmap(fDisplay, fPixmap); + fPixmap = 0; + } + + XCloseDisplay(fDisplay); + fDisplay = nullptr; + } +} + +void GLXGLContext::onPlatformMakeCurrent() const { + if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { + SkDebugf("Could not set the context.\n"); + } +} + +void GLXGLContext::onPlatformSwapBuffers() const { + glXSwapBuffers(fDisplay, fGlxPixmap); +} + +GrGLFuncPtr GLXGLContext::onPlatformGetProcAddress(const char* procName) const { + return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName)); +} + +} // anonymous namespace + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + GLXGLContext* glxShareContext = reinterpret_cast<GLXGLContext*>(shareContext); + GLXGLContext *ctx = new GLXGLContext(forcedGpuAPI, glxShareContext); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} diff --git a/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm b/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm new file mode 100644 index 0000000000..54dc59af03 --- /dev/null +++ b/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm @@ -0,0 +1,106 @@ + +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/SkGLContext.h" +#import <OpenGLES/EAGL.h> +#include <dlfcn.h> + +#define EAGLCTX ((EAGLContext*)(fEAGLContext)) + +namespace { + +class IOSGLContext : public SkGLContext { +public: + IOSGLContext(); + ~IOSGLContext() override; + +private: + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; + + void* fEAGLContext; + void* fGLLibrary; +}; + +IOSGLContext::IOSGLContext() + : fEAGLContext(NULL) + , fGLLibrary(RTLD_DEFAULT) { + + fEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + [EAGLContext setCurrentContext:EAGLCTX]; + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface()); + if (NULL == gl.get()) { + SkDebugf("Failed to create gl interface"); + this->destroyGLContext(); + return; + } + if (!gl->validate()) { + SkDebugf("Failed to validate gl interface"); + this->destroyGLContext(); + return; + } + + fGLLibrary = dlopen( + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", + RTLD_LAZY); + + this->init(gl.release()); +} + +IOSGLContext::~IOSGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void IOSGLContext::destroyGLContext() { + if (fEAGLContext) { + if ([EAGLContext currentContext] == EAGLCTX) { + [EAGLContext setCurrentContext:nil]; + } + [EAGLCTX release]; + fEAGLContext = NULL; + } + if (RTLD_DEFAULT != fGLLibrary) { + dlclose(fGLLibrary); + } +} + + +void IOSGLContext::onPlatformMakeCurrent() const { + if (![EAGLContext setCurrentContext:EAGLCTX]) { + SkDebugf("Could not set the context.\n"); + } +} + +void IOSGLContext::onPlatformSwapBuffers() const { } + +GrGLFuncPtr IOSGLContext::onPlatformGetProcAddress(const char* procName) const { + return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName)); +} + +} // anonymous namespace + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + SkASSERT(!shareContext); + if (shareContext) { + return NULL; + } + if (kGL_GrGLStandard == forcedGpuAPI) { + return NULL; + } + IOSGLContext* ctx = new IOSGLContext; + if (!ctx->isValid()) { + delete ctx; + return NULL; + } + return ctx; +} diff --git a/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp b/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp new file mode 100644 index 0000000000..0ca62ad1cc --- /dev/null +++ b/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkTypes.h" +#if defined(SK_BUILD_FOR_MAC) + +#include "gl/SkGLContext.h" +#include "AvailabilityMacros.h" + +#include <OpenGL/OpenGL.h> +#include <dlfcn.h> + +namespace { +class MacGLContext : public SkGLContext { +public: + MacGLContext(); + ~MacGLContext() override; + +private: + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; + + CGLContextObj fContext; + void* fGLLibrary; +}; + +MacGLContext::MacGLContext() + : fContext(nullptr) + , fGLLibrary(RTLD_DEFAULT) { + CGLPixelFormatAttribute attributes[] = { +#if MAC_OS_X_VERSION_10_7 + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, +#endif + kCGLPFADoubleBuffer, + (CGLPixelFormatAttribute)0 + }; + CGLPixelFormatObj pixFormat; + GLint npix; + + CGLChoosePixelFormat(attributes, &pixFormat, &npix); + + if (nullptr == pixFormat) { + SkDebugf("CGLChoosePixelFormat failed."); + return; + } + + CGLCreateContext(pixFormat, nullptr, &fContext); + CGLReleasePixelFormat(pixFormat); + + if (nullptr == fContext) { + SkDebugf("CGLCreateContext failed."); + return; + } + + CGLSetCurrentContext(fContext); + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface()); + if (nullptr == gl.get()) { + SkDebugf("Context could not create GL interface.\n"); + this->destroyGLContext(); + return; + } + if (!gl->validate()) { + SkDebugf("Context could not validate GL interface.\n"); + this->destroyGLContext(); + return; + } + + fGLLibrary = dlopen( + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", + RTLD_LAZY); + + this->init(gl.release()); +} + +MacGLContext::~MacGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void MacGLContext::destroyGLContext() { + if (fContext) { + CGLReleaseContext(fContext); + fContext = nullptr; + } + if (RTLD_DEFAULT != fGLLibrary) { + dlclose(fGLLibrary); + } +} + +void MacGLContext::onPlatformMakeCurrent() const { + CGLSetCurrentContext(fContext); +} + +void MacGLContext::onPlatformSwapBuffers() const { + CGLFlushDrawable(fContext); +} + +GrGLFuncPtr MacGLContext::onPlatformGetProcAddress(const char* procName) const { + return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName)); +} + +} // anonymous namespace + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + SkASSERT(!shareContext); + if (shareContext) { + return nullptr; + } + + if (kGLES_GrGLStandard == forcedGpuAPI) { + return nullptr; + } + MacGLContext* ctx = new MacGLContext; + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} + +#endif//defined(SK_BUILD_FOR_MAC) diff --git a/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp new file mode 100644 index 0000000000..6a04d4f387 --- /dev/null +++ b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/GrGLAssembleInterface.h" +#include "../GrGLUtil.h" + +#include "osmesa_wrapper.h" + +static GrGLFuncPtr osmesa_get(void* ctx, const char name[]) { + SkASSERT(nullptr == ctx); + SkASSERT(OSMesaGetCurrentContext()); + return OSMesaGetProcAddress(name); +} + +const GrGLInterface* GrGLCreateMesaInterface() { + if (nullptr == OSMesaGetCurrentContext()) { + return nullptr; + } + return GrGLAssembleInterface(nullptr, osmesa_get); +} diff --git a/src/gpu/gl/mesa/SkMesaGLContext.cpp b/src/gpu/gl/mesa/SkMesaGLContext.cpp new file mode 100644 index 0000000000..eeccbd64ae --- /dev/null +++ b/src/gpu/gl/mesa/SkMesaGLContext.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <GL/osmesa.h> + +#include "gl/mesa/SkMesaGLContext.h" +#include "gl/GrGLDefines.h" + +static const GrGLint gBOGUS_SIZE = 16; + +SkMesaGLContext::SkMesaGLContext() + : fContext(static_cast<Context>(0)) + , fImage(nullptr) { + GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext)); + + /* Create an RGBA-mode context */ +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + /* specify Z, stencil, accum sizes */ + fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr); +#else + fContext = (Context)OSMesaCreateContext(OSMESA_BGRA, nullptr); +#endif + if (!fContext) { + SkDebugf("OSMesaCreateContext failed!\n"); + this->destroyGLContext(); + return; + } + // Allocate the image buffer + fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE * + 4 * sizeof(GrGLubyte)); + if (!fImage) { + SkDebugf("Alloc image buffer failed!\n"); + this->destroyGLContext(); + return; + } + + // Bind the buffer to the context and make it current + if (!OSMesaMakeCurrent((OSMesaContext)fContext, + fImage, + GR_GL_UNSIGNED_BYTE, + gBOGUS_SIZE, + gBOGUS_SIZE)) { + SkDebugf("OSMesaMakeCurrent failed!\n"); + this->destroyGLContext(); + return; + } + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateMesaInterface()); + if (nullptr == gl.get()) { + SkDebugf("Could not create GL interface!\n"); + this->destroyGLContext(); + return; + } + + if (!gl->validate()) { + SkDebugf("Could not validate GL interface!\n"); + this->destroyGLContext(); + return; + } + + this->init(gl.release()); +} + +SkMesaGLContext::~SkMesaGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void SkMesaGLContext::destroyGLContext() { + if (fImage) { + sk_free(fImage); + fImage = nullptr; + } + + if (fContext) { + OSMesaDestroyContext((OSMesaContext)fContext); + fContext = static_cast<Context>(0); + } +} + + + +void SkMesaGLContext::onPlatformMakeCurrent() const { + if (fContext) { + if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage, + GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) { + SkDebugf("Could not make MESA context current."); + } + } +} + +void SkMesaGLContext::onPlatformSwapBuffers() const { } + +GrGLFuncPtr SkMesaGLContext::onPlatformGetProcAddress(const char* procName) const { + return OSMesaGetProcAddress(procName); +} diff --git a/src/gpu/gl/mesa/SkMesaGLContext.h b/src/gpu/gl/mesa/SkMesaGLContext.h new file mode 100644 index 0000000000..7f700b6536 --- /dev/null +++ b/src/gpu/gl/mesa/SkMesaGLContext.h @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkMesaGLContext_DEFINED +#define SkMesaGLContext_DEFINED + +#include "gl/SkGLContext.h" + +#if SK_MESA + +class SkMesaGLContext : public SkGLContext { +private: + typedef intptr_t Context; + +public: + ~SkMesaGLContext() override; + + static SkMesaGLContext* Create() { + SkMesaGLContext* ctx = new SkMesaGLContext; + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; + } + +private: + SkMesaGLContext(); + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; + + Context fContext; + GrGLubyte *fImage; +}; + +#endif + +#endif diff --git a/src/gpu/gl/nacl/SkCreatePlatformGLContext_nacl.cpp b/src/gpu/gl/nacl/SkCreatePlatformGLContext_nacl.cpp new file mode 100644 index 0000000000..855f0dc124 --- /dev/null +++ b/src/gpu/gl/nacl/SkCreatePlatformGLContext_nacl.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "gl/SkGLContext.h" + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + SkASSERT(!shareContext); + return nullptr; +} diff --git a/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp b/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp new file mode 100644 index 0000000000..e99aa15eb7 --- /dev/null +++ b/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gl/SkGLContext.h" + +#include <windows.h> +#include <GL/GL.h> +#include "win/SkWGL.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +namespace { + +class WinGLContext : public SkGLContext { +public: + WinGLContext(GrGLStandard forcedGpuAPI); + ~WinGLContext() override; + +private: + void destroyGLContext(); + + void onPlatformMakeCurrent() const override; + void onPlatformSwapBuffers() const override; + GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; + + HWND fWindow; + HDC fDeviceContext; + HGLRC fGlRenderContext; + static ATOM gWC; + SkWGLPbufferContext* fPbufferContext; +}; + +ATOM WinGLContext::gWC = 0; + +WinGLContext::WinGLContext(GrGLStandard forcedGpuAPI) + : fWindow(nullptr) + , fDeviceContext(nullptr) + , fGlRenderContext(0) + , fPbufferContext(nullptr) { + HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr); + + if (!gWC) { + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = nullptr; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + wc.hInstance = hInstance; + wc.lpfnWndProc = (WNDPROC) DefWindowProc; + wc.lpszClassName = TEXT("Griffin"); + wc.lpszMenuName = nullptr; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + + gWC = RegisterClass(&wc); + if (!gWC) { + SkDebugf("Could not register window class.\n"); + return; + } + } + + if (!(fWindow = CreateWindow(TEXT("Griffin"), + TEXT("The Invisible Man"), + WS_OVERLAPPEDWINDOW, + 0, 0, 1, 1, + nullptr, nullptr, + hInstance, nullptr))) { + SkDebugf("Could not create window.\n"); + return; + } + + if (!(fDeviceContext = GetDC(fWindow))) { + SkDebugf("Could not get device context.\n"); + this->destroyGLContext(); + return; + } + // Requesting a Core profile would bar us from using NVPR. So we request + // compatibility profile or GL ES. + SkWGLContextRequest contextType = + kGLES_GrGLStandard == forcedGpuAPI ? + kGLES_SkWGLContextRequest : kGLPreferCompatibilityProfile_SkWGLContextRequest; + + fPbufferContext = SkWGLPbufferContext::Create(fDeviceContext, 0, contextType); + + HDC dc; + HGLRC glrc; + + if (nullptr == fPbufferContext) { + if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, contextType))) { + SkDebugf("Could not create rendering context.\n"); + this->destroyGLContext(); + return; + } + dc = fDeviceContext; + glrc = fGlRenderContext; + } else { + ReleaseDC(fWindow, fDeviceContext); + fDeviceContext = 0; + DestroyWindow(fWindow); + fWindow = 0; + + dc = fPbufferContext->getDC(); + glrc = fPbufferContext->getGLRC(); + } + + if (!(wglMakeCurrent(dc, glrc))) { + SkDebugf("Could not set the context.\n"); + this->destroyGLContext(); + return; + } + + SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface()); + if (nullptr == gl.get()) { + SkDebugf("Could not create GL interface.\n"); + this->destroyGLContext(); + return; + } + if (!gl->validate()) { + SkDebugf("Could not validate GL interface.\n"); + this->destroyGLContext(); + return; + } + + this->init(gl.release()); +} + +WinGLContext::~WinGLContext() { + this->teardown(); + this->destroyGLContext(); +} + +void WinGLContext::destroyGLContext() { + SkSafeSetNull(fPbufferContext); + if (fGlRenderContext) { + wglDeleteContext(fGlRenderContext); + fGlRenderContext = 0; + } + if (fWindow && fDeviceContext) { + ReleaseDC(fWindow, fDeviceContext); + fDeviceContext = 0; + } + if (fWindow) { + DestroyWindow(fWindow); + fWindow = 0; + } +} + +void WinGLContext::onPlatformMakeCurrent() const { + HDC dc; + HGLRC glrc; + + if (nullptr == fPbufferContext) { + dc = fDeviceContext; + glrc = fGlRenderContext; + } else { + dc = fPbufferContext->getDC(); + glrc = fPbufferContext->getGLRC(); + } + + if (!wglMakeCurrent(dc, glrc)) { + SkDebugf("Could not create rendering context.\n"); + } +} + +void WinGLContext::onPlatformSwapBuffers() const { + HDC dc; + + if (nullptr == fPbufferContext) { + dc = fDeviceContext; + } else { + dc = fPbufferContext->getDC(); + } + if (!SwapBuffers(dc)) { + SkDebugf("Could not complete SwapBuffers.\n"); + } +} + +GrGLFuncPtr WinGLContext::onPlatformGetProcAddress(const char* name) const { + return reinterpret_cast<GrGLFuncPtr>(wglGetProcAddress(name)); +} + +} // anonymous namespace + +SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { + SkASSERT(!shareContext); + if (shareContext) { + return nullptr; + } + WinGLContext* ctx = new WinGLContext(forcedGpuAPI); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} diff --git a/src/views/win/SkOSWindow_win.cpp b/src/views/win/SkOSWindow_win.cpp index eba3a77e4d..8b0211d64b 100644 --- a/src/views/win/SkOSWindow_win.cpp +++ b/src/views/win/SkOSWindow_win.cpp @@ -395,36 +395,6 @@ void SkOSWindow::presentGL() { #if SK_ANGLE -void* get_angle_egl_display(void* nativeDisplay) { - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; - eglGetPlatformDisplayEXT = - (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); - - // We expect ANGLE to support this extension - if (!eglGetPlatformDisplayEXT) { - return EGL_NO_DISPLAY; - } - - EGLDisplay display = EGL_NO_DISPLAY; - // Try for an ANGLE D3D11 context, fall back to D3D9, and finally GL. - EGLint attribs[3][3] = { - { - EGL_PLATFORM_ANGLE_TYPE_ANGLE, - EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - EGL_NONE - }, - { - EGL_PLATFORM_ANGLE_TYPE_ANGLE, - EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, - EGL_NONE - }, - }; - for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) { - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]); - } - return display; -} - bool create_ANGLE(EGLNativeWindowType hWnd, int msaaSampleCount, EGLDisplay* eglDisplay, @@ -448,7 +418,7 @@ bool create_ANGLE(EGLNativeWindowType hWnd, EGL_NONE, EGL_NONE }; - EGLDisplay display = get_angle_egl_display(GetDC(hWnd), false); + EGLDisplay display = SkANGLEGLContext::GetD3DEGLDisplay(GetDC(hWnd), false); if (EGL_NO_DISPLAY == display) { SkDebugf("Could not create ANGLE egl display!\n"); |