/*
|
* 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
|