| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| |
| #include "GrBackendSurface.h" |
| |
| #include "gl/GrGLUtil.h" |
| |
| #ifdef SK_VULKAN |
| #include "vk/GrVkImageLayout.h" |
| #include "vk/GrVkTypes.h" |
| #include "vk/GrVkUtil.h" |
| #endif |
| #ifdef SK_METAL |
| #include "mtl/GrMtlTypes.h" |
| #include "mtl/GrMtlCppUtil.h" |
| #endif |
| |
| GrBackendFormat::GrBackendFormat(GrGLenum format, GrGLenum target) |
| : fBackend(GrBackendApi::kOpenGL) |
| , fValid(true) |
| , fGLFormat(format) { |
| switch (target) { |
| case GR_GL_TEXTURE_2D: |
| fTextureType = GrTextureType::k2D; |
| break; |
| case GR_GL_TEXTURE_RECTANGLE: |
| fTextureType = GrTextureType::kRectangle; |
| break; |
| case GR_GL_TEXTURE_EXTERNAL: |
| fTextureType = GrTextureType::kExternal; |
| break; |
| default: |
| SK_ABORT("Unexpected texture target"); |
| } |
| } |
| |
| const GrGLenum* GrBackendFormat::getGLFormat() const { |
| if (this->isValid() && GrBackendApi::kOpenGL == fBackend) { |
| return &fGLFormat; |
| } |
| return nullptr; |
| } |
| |
| const GrGLenum* GrBackendFormat::getGLTarget() const { |
| if (this->isValid() && GrBackendApi::kOpenGL == fBackend) { |
| static constexpr GrGLenum k2D = GR_GL_TEXTURE_2D; |
| static constexpr GrGLenum kRect = GR_GL_TEXTURE_RECTANGLE; |
| static constexpr GrGLenum kExternal = GR_GL_TEXTURE_EXTERNAL; |
| switch (fTextureType) { |
| case GrTextureType::k2D: |
| return &k2D; |
| case GrTextureType::kRectangle: |
| return &kRect; |
| case GrTextureType::kExternal: |
| return &kExternal; |
| } |
| } |
| return nullptr; |
| } |
| |
| GrBackendFormat GrBackendFormat::MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo) { |
| #ifdef SK_BUILD_FOR_ANDROID |
| return GrBackendFormat(VK_FORMAT_UNDEFINED, ycbcrInfo); |
| #else |
| return GrBackendFormat(); |
| #endif |
| } |
| |
| GrBackendFormat::GrBackendFormat(VkFormat vkFormat, const GrVkYcbcrConversionInfo& ycbcrInfo) |
| : fBackend(GrBackendApi::kVulkan) |
| #ifdef SK_VULKAN |
| , fValid(true) |
| #else |
| , fValid(false) |
| #endif |
| , fTextureType(GrTextureType::k2D) { |
| fVk.fFormat = vkFormat; |
| fVk.fYcbcrConversionInfo = ycbcrInfo; |
| } |
| |
| const VkFormat* GrBackendFormat::getVkFormat() const { |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| return &fVk.fFormat; |
| } |
| return nullptr; |
| } |
| |
| const GrVkYcbcrConversionInfo* GrBackendFormat::getVkYcbcrConversionInfo() const { |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| return &fVk.fYcbcrConversionInfo; |
| } |
| return nullptr; |
| } |
| |
| #ifdef SK_METAL |
| GrBackendFormat::GrBackendFormat(GrMTLPixelFormat mtlFormat) |
| : fBackend(GrBackendApi::kMetal) |
| , fValid(true) |
| , fMtlFormat(mtlFormat) |
| , fTextureType(GrTextureType::k2D) { |
| } |
| |
| const GrMTLPixelFormat* GrBackendFormat::getMtlFormat() const { |
| if (this->isValid() && GrBackendApi::kMetal == fBackend) { |
| return &fMtlFormat; |
| } |
| return nullptr; |
| } |
| #endif |
| |
| GrBackendFormat::GrBackendFormat(GrPixelConfig config) |
| : fBackend(GrBackendApi::kMock) |
| , fValid(true) |
| , fMockFormat(config) |
| , fTextureType(GrTextureType::k2D) { |
| } |
| |
| const GrPixelConfig* GrBackendFormat::getMockFormat() const { |
| if (this->isValid() && GrBackendApi::kMock == fBackend) { |
| return &fMockFormat; |
| } |
| return nullptr; |
| } |
| |
| GrBackendFormat GrBackendFormat::makeTexture2D() const { |
| // TODO: once we support ycbcr conversions in Vulkan we need to check if we are using an |
| // external format since they will not be able to be made into a Texture2D. |
| GrBackendFormat copy = *this; |
| copy.fTextureType = GrTextureType::k2D; |
| return copy; |
| } |
| |
| bool GrBackendFormat::operator==(const GrBackendFormat& that) const { |
| // Invalid GrBackendFormats are never equal to anything. |
| if (!fValid || !that.fValid) { |
| return false; |
| } |
| |
| if (fBackend != that.fBackend) { |
| return false; |
| } |
| |
| switch (fBackend) { |
| case GrBackendApi::kOpenGL: |
| return fGLFormat == that.fGLFormat; |
| case GrBackendApi::kVulkan: |
| #ifdef SK_VULKAN |
| return fVk.fFormat == that.fVk.fFormat && |
| fVk.fYcbcrConversionInfo == that.fVk.fYcbcrConversionInfo; |
| #endif |
| break; |
| #ifdef SK_METAL |
| case GrBackendApi::kMetal: |
| return fMtlFormat == that.fMtlFormat; |
| #endif |
| break; |
| case GrBackendApi::kMock: |
| return fMockFormat == that.fMockFormat; |
| default: |
| SK_ABORT("Unknown GrBackend"); |
| } |
| return false; |
| } |
| |
| GrBackendTexture::GrBackendTexture(int width, |
| int height, |
| const GrVkImageInfo& vkInfo) |
| #ifdef SK_VULKAN |
| : GrBackendTexture(width, height, vkInfo, |
| sk_sp<GrVkImageLayout>(new GrVkImageLayout(vkInfo.fImageLayout))) {} |
| #else |
| : fIsValid(false) {} |
| #endif |
| |
| #ifdef SK_VULKAN |
| GrBackendTexture::GrBackendTexture(int width, |
| int height, |
| const GrVkImageInfo& vkInfo, |
| sk_sp<GrVkImageLayout> layout) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fConfig(kUnknown_GrPixelConfig) |
| , fMipMapped(GrMipMapped(vkInfo.fLevelCount > 1)) |
| , fBackend(GrBackendApi::kVulkan) |
| , fVkInfo(vkInfo, layout.release()) { |
| } |
| #endif |
| |
| #ifdef SK_METAL |
| GrBackendTexture::GrBackendTexture(int width, |
| int height, |
| GrMipMapped mipMapped, |
| const GrMtlTextureInfo& mtlInfo) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fConfig(GrPixelConfig::kUnknown_GrPixelConfig) |
| , fMipMapped(mipMapped) |
| , fBackend(GrBackendApi::kMetal) |
| , fMtlInfo(mtlInfo) {} |
| #endif |
| |
| GrBackendTexture::GrBackendTexture(int width, |
| int height, |
| GrMipMapped mipMapped, |
| const GrGLTextureInfo& glInfo) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fConfig(kUnknown_GrPixelConfig) |
| , fMipMapped(mipMapped) |
| , fBackend(GrBackendApi::kOpenGL) |
| , fGLInfo(glInfo) {} |
| |
| GrBackendTexture::GrBackendTexture(int width, |
| int height, |
| GrMipMapped mipMapped, |
| const GrMockTextureInfo& mockInfo) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fConfig(mockInfo.fConfig) |
| , fMipMapped(mipMapped) |
| , fBackend(GrBackendApi::kMock) |
| , fMockInfo(mockInfo) {} |
| |
| GrBackendTexture::~GrBackendTexture() { |
| this->cleanup(); |
| } |
| |
| void GrBackendTexture::cleanup() { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| fVkInfo.cleanup(); |
| } |
| #endif |
| } |
| |
| GrBackendTexture::GrBackendTexture(const GrBackendTexture& that) : fIsValid(false) { |
| *this = that; |
| } |
| |
| GrBackendTexture& GrBackendTexture::operator=(const GrBackendTexture& that) { |
| if (!that.isValid()) { |
| this->cleanup(); |
| fIsValid = false; |
| return *this; |
| } |
| fWidth = that.fWidth; |
| fHeight = that.fHeight; |
| fConfig = that.fConfig; |
| fMipMapped = that.fMipMapped; |
| fBackend = that.fBackend; |
| |
| switch (that.fBackend) { |
| case GrBackendApi::kOpenGL: |
| fGLInfo = that.fGLInfo; |
| break; |
| case GrBackendApi::kVulkan: |
| #ifdef SK_VULKAN |
| fVkInfo.assign(that.fVkInfo, this->isValid()); |
| #endif |
| break; |
| #ifdef SK_METAL |
| case GrBackendApi::kMetal: |
| fMtlInfo = that.fMtlInfo; |
| break; |
| #endif |
| case GrBackendApi::kMock: |
| fMockInfo = that.fMockInfo; |
| break; |
| default: |
| SK_ABORT("Unknown GrBackend"); |
| } |
| fIsValid = that.fIsValid; |
| return *this; |
| } |
| |
| bool GrBackendTexture::getVkImageInfo(GrVkImageInfo* outInfo) const { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| *outInfo = fVkInfo.snapImageInfo(); |
| return true; |
| } |
| #endif |
| return false; |
| } |
| |
| void GrBackendTexture::setVkImageLayout(VkImageLayout layout) { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| fVkInfo.setImageLayout(layout); |
| } |
| #endif |
| } |
| |
| // We need a stubbed version of GrVkImageLayout for non vulkan builds |
| #ifndef SK_VULKAN |
| class GrVkImageLayout : public SkRefCnt { |
| GrVkImageLayout(VkImageLayout layout) {} |
| }; |
| #endif |
| |
| sk_sp<GrVkImageLayout> GrBackendTexture::getGrVkImageLayout() const { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| return fVkInfo.getGrVkImageLayout(); |
| } |
| #endif |
| return nullptr; |
| } |
| |
| #ifdef SK_METAL |
| bool GrBackendTexture::getMtlTextureInfo(GrMtlTextureInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kMetal == fBackend) { |
| *outInfo = fMtlInfo; |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| bool GrBackendTexture::getGLTextureInfo(GrGLTextureInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kOpenGL == fBackend) { |
| *outInfo = fGLInfo; |
| return true; |
| } |
| return false; |
| } |
| |
| bool GrBackendTexture::getMockTextureInfo(GrMockTextureInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kMock == fBackend) { |
| *outInfo = fMockInfo; |
| return true; |
| } |
| return false; |
| } |
| |
| GrBackendFormat GrBackendTexture::getBackendFormat() const { |
| if (!this->isValid()) { |
| return GrBackendFormat(); |
| } |
| switch (fBackend) { |
| case GrBackendApi::kOpenGL: |
| return GrBackendFormat::MakeGL(fGLInfo.fFormat, fGLInfo.fTarget); |
| #ifdef SK_VULKAN |
| case GrBackendApi::kVulkan: { |
| auto info = fVkInfo.snapImageInfo(); |
| if (info.fYcbcrConversionInfo.isValid()) { |
| SkASSERT(info.fFormat == VK_FORMAT_UNDEFINED); |
| return GrBackendFormat::MakeVk(info.fYcbcrConversionInfo); |
| } |
| return GrBackendFormat::MakeVk(info.fFormat); |
| } |
| #endif |
| #ifdef SK_METAL |
| case GrBackendApi::kMetal: { |
| GrMtlTextureInfo mtlInfo; |
| SkAssertResult(this->getMtlTextureInfo(&mtlInfo)); |
| return GrBackendFormat::MakeMtl(GrGetMTLPixelFormatFromMtlTextureInfo(mtlInfo)); |
| } |
| #endif |
| case GrBackendApi::kMock: |
| return GrBackendFormat::MakeMock(fMockInfo.fConfig); |
| default: |
| return GrBackendFormat(); |
| } |
| } |
| |
| #if GR_TEST_UTILS |
| bool GrBackendTexture::TestingOnly_Equals(const GrBackendTexture& t0, const GrBackendTexture& t1) { |
| if (!t0.isValid() || !t1.isValid()) { |
| return false; // two invalid backend textures are not considered equal |
| } |
| |
| if (t0.fWidth != t1.fWidth || |
| t0.fHeight != t1.fHeight || |
| t0.fConfig != t1.fConfig || |
| t0.fMipMapped != t1.fMipMapped || |
| t0.fBackend != t1.fBackend) { |
| return false; |
| } |
| |
| switch (t0.fBackend) { |
| case GrBackendApi::kOpenGL: |
| return t0.fGLInfo == t1.fGLInfo; |
| case GrBackendApi::kMock: |
| return t0.fMockInfo == t1.fMockInfo; |
| case GrBackendApi::kVulkan: |
| #ifdef SK_VULKAN |
| return t0.fVkInfo == t1.fVkInfo; |
| #else |
| // fall through |
| #endif |
| case GrBackendApi::kMetal: |
| #ifdef SK_METAL |
| return t0.fMtlInfo == t1.fMtlInfo; |
| #else |
| // fall through |
| #endif |
| default: |
| return false; |
| } |
| |
| SkASSERT(0); |
| return false; |
| } |
| #endif |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| int stencilBits, |
| const GrVkImageInfo& vkInfo) |
| : GrBackendRenderTarget(width, height, sampleCnt, vkInfo) { |
| // This is a deprecated constructor that takes a bogus stencil bits. |
| SkASSERT(0 == stencilBits); |
| } |
| |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| const GrVkImageInfo& vkInfo) |
| #ifdef SK_VULKAN |
| : GrBackendRenderTarget(width, height, sampleCnt, vkInfo, |
| sk_sp<GrVkImageLayout>(new GrVkImageLayout(vkInfo.fImageLayout))) {} |
| #else |
| : fIsValid(false) {} |
| #endif |
| |
| #ifdef SK_VULKAN |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| const GrVkImageInfo& vkInfo, |
| sk_sp<GrVkImageLayout> layout) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fSampleCnt(SkTMax(1, sampleCnt)) |
| , fStencilBits(0) // We always create stencil buffers internally for vulkan |
| , fConfig(kUnknown_GrPixelConfig) |
| , fBackend(GrBackendApi::kVulkan) |
| , fVkInfo(vkInfo, layout.release()) {} |
| #endif |
| |
| #ifdef SK_METAL |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| const GrMtlTextureInfo& mtlInfo) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fSampleCnt(SkTMax(1, sampleCnt)) |
| , fStencilBits(0) |
| , fConfig(GrPixelConfig::kUnknown_GrPixelConfig) |
| , fBackend(GrBackendApi::kMetal) |
| , fMtlInfo(mtlInfo) {} |
| #endif |
| |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| int stencilBits, |
| const GrGLFramebufferInfo& glInfo) |
| : fWidth(width) |
| , fHeight(height) |
| , fSampleCnt(SkTMax(1, sampleCnt)) |
| , fStencilBits(stencilBits) |
| , fConfig(kUnknown_GrPixelConfig) |
| , fBackend(GrBackendApi::kOpenGL) |
| , fGLInfo(glInfo) { |
| fIsValid = SkToBool(glInfo.fFormat); // the glInfo must have a valid format |
| } |
| |
| GrBackendRenderTarget::GrBackendRenderTarget(int width, |
| int height, |
| int sampleCnt, |
| int stencilBits, |
| const GrMockRenderTargetInfo& mockInfo) |
| : fIsValid(true) |
| , fWidth(width) |
| , fHeight(height) |
| , fSampleCnt(SkTMax(1, sampleCnt)) |
| , fStencilBits(stencilBits) |
| , fConfig(mockInfo.fConfig) |
| , fMockInfo(mockInfo) {} |
| |
| GrBackendRenderTarget::~GrBackendRenderTarget() { |
| this->cleanup(); |
| } |
| |
| void GrBackendRenderTarget::cleanup() { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| fVkInfo.cleanup(); |
| } |
| #endif |
| } |
| |
| GrBackendRenderTarget::GrBackendRenderTarget(const GrBackendRenderTarget& that) : fIsValid(false) { |
| *this = that; |
| } |
| |
| GrBackendRenderTarget& GrBackendRenderTarget::operator=(const GrBackendRenderTarget& that) { |
| if (!that.isValid()) { |
| this->cleanup(); |
| fIsValid = false; |
| return *this; |
| } |
| fWidth = that.fWidth; |
| fHeight = that.fHeight; |
| fSampleCnt = that.fSampleCnt; |
| fStencilBits = that.fStencilBits; |
| fConfig = that.fConfig; |
| fBackend = that.fBackend; |
| |
| switch (that.fBackend) { |
| case GrBackendApi::kOpenGL: |
| fGLInfo = that.fGLInfo; |
| break; |
| case GrBackendApi::kVulkan: |
| #ifdef SK_VULKAN |
| fVkInfo.assign(that.fVkInfo, this->isValid()); |
| #endif |
| break; |
| #ifdef SK_METAL |
| case GrBackendApi::kMetal: |
| fMtlInfo = that.fMtlInfo; |
| break; |
| #endif |
| case GrBackendApi::kMock: |
| fMockInfo = that.fMockInfo; |
| break; |
| default: |
| SK_ABORT("Unknown GrBackend"); |
| } |
| fIsValid = that.fIsValid; |
| return *this; |
| } |
| |
| bool GrBackendRenderTarget::getVkImageInfo(GrVkImageInfo* outInfo) const { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| *outInfo = fVkInfo.snapImageInfo(); |
| return true; |
| } |
| #endif |
| return false; |
| } |
| |
| void GrBackendRenderTarget::setVkImageLayout(VkImageLayout layout) { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| fVkInfo.setImageLayout(layout); |
| } |
| #endif |
| } |
| |
| sk_sp<GrVkImageLayout> GrBackendRenderTarget::getGrVkImageLayout() const { |
| #ifdef SK_VULKAN |
| if (this->isValid() && GrBackendApi::kVulkan == fBackend) { |
| return fVkInfo.getGrVkImageLayout(); |
| } |
| #endif |
| return nullptr; |
| } |
| |
| #ifdef SK_METAL |
| bool GrBackendRenderTarget::getMtlTextureInfo(GrMtlTextureInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kMetal == fBackend) { |
| *outInfo = fMtlInfo; |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| bool GrBackendRenderTarget::getGLFramebufferInfo(GrGLFramebufferInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kOpenGL == fBackend) { |
| *outInfo = fGLInfo; |
| return true; |
| } |
| return false; |
| } |
| |
| bool GrBackendRenderTarget::getMockRenderTargetInfo(GrMockRenderTargetInfo* outInfo) const { |
| if (this->isValid() && GrBackendApi::kMock == fBackend) { |
| *outInfo = fMockInfo; |
| return true; |
| } |
| return false; |
| } |
| |
| #if GR_TEST_UTILS |
| bool GrBackendRenderTarget::TestingOnly_Equals(const GrBackendRenderTarget& r0, |
| const GrBackendRenderTarget& r1) { |
| if (!r0.isValid() || !r1.isValid()) { |
| return false; // two invalid backend rendertargets are not considered equal |
| } |
| |
| if (r0.fWidth != r1.fWidth || |
| r0.fHeight != r1.fHeight || |
| r0.fSampleCnt != r1.fSampleCnt || |
| r0.fStencilBits != r1.fStencilBits || |
| r0.fConfig != r1.fConfig || |
| r0.fBackend != r1.fBackend) { |
| return false; |
| } |
| |
| switch (r0.fBackend) { |
| case GrBackendApi::kOpenGL: |
| return r0.fGLInfo == r1.fGLInfo; |
| case GrBackendApi::kMock: |
| return r0.fMockInfo == r1.fMockInfo; |
| case GrBackendApi::kVulkan: |
| #ifdef SK_VULKAN |
| return r0.fVkInfo == r1.fVkInfo; |
| #else |
| // fall through |
| #endif |
| case GrBackendApi::kMetal: |
| #ifdef SK_METAL |
| return r0.fMtlInfo == r1.fMtlInfo; |
| #else |
| // fall through |
| #endif |
| default: |
| return false; |
| } |
| |
| SkASSERT(0); |
| return false; |
| } |
| #endif |