Add support for RGB config to Vulkan.

This adds support for RGB, but from investigating, most desktops don't
support RGB at all, and on Android it is usually support as a linear
format and not necessarily an optimal one. So until we get better support
for linear formats this CL doesn't necessarily add more feature support to
our vulkan backend.

Bug: skia:8349
Change-Id: I1066ddafa660a1ef1d90dbf3e127e067d6132d45
Reviewed-on: https://skia-review.googlesource.com/156600
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Greg Daniel 2018-09-25 13:50:40 -04:00 committed by Skia Commit-Bot
parent d37570b325
commit a8b20a1af7
7 changed files with 169 additions and 41 deletions

View File

@ -712,8 +712,10 @@ bool validate_image_info(VkFormat format, SkColorType ct, GrPixelConfig* config)
}
break;
case kRGB_888x_SkColorType:
// TODO: VK_FORMAT_R8G8B8_UNORM
return false;
if (VK_FORMAT_R8G8B8_UNORM == format) {
*config = kRGB_888_GrPixelConfig;
}
break;
case kBGRA_8888_SkColorType:
if (VK_FORMAT_B8G8R8A8_UNORM == format) {
*config = kBGRA_8888_GrPixelConfig;

View File

@ -584,6 +584,24 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
return false;
}
if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
return false;
}
// For RGB_888x src data we are uploading it first to an RGBA texture and then copying it to the
// dst RGB texture. Thus we do not upload mip levels for that.
if (dataColorType == GrColorType::kRGB_888x) {
SkASSERT(tex->imageFormat() == VK_FORMAT_R8G8B8_UNORM &&
tex->config() == kRGB_888_GrPixelConfig);
// First check that we'll be able to do the copy to the to the R8G8B8 image in the end via a
// blit or draw.
if (!this->vkCaps().configCanBeDstofBlit(kRGB_888_GrPixelConfig, tex->isLinearTiled()) &&
!this->vkCaps().maxRenderTargetSampleCount(kRGB_888_GrPixelConfig)) {
return false;
}
mipLevelCount = 1;
}
SkASSERT(this->caps()->isConfigTexturable(tex->config()));
int bpp = GrColorTypeBytesPerPixel(dataColorType);
@ -592,17 +610,15 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
// Because of this we need to make a non-const shallow copy of texels.
SkAutoTMalloc<GrMipLevel> texelsShallowCopy;
if (mipLevelCount) {
texelsShallowCopy.reset(mipLevelCount);
memcpy(texelsShallowCopy.get(), texels, mipLevelCount*sizeof(GrMipLevel));
}
SkTArray<size_t> individualMipOffsets(mipLevelCount);
individualMipOffsets.push_back(0);
size_t combinedBufferSize = width * bpp * height;
int currentWidth = width;
int currentHeight = height;
if (mipLevelCount > 0 && !texelsShallowCopy[0].fPixels) {
if (!texelsShallowCopy[0].fPixels) {
combinedBufferSize = 0;
}
@ -638,12 +654,51 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
return false;
}
int uploadLeft = left;
int uploadTop = top;
GrVkTexture* uploadTexture = tex;
// For uploading RGB_888x data to an R8G8B8_UNORM texture we must first upload the data to an
// R8G8B8A8_UNORM image and then copy it.
sk_sp<GrVkTexture> copyTexture;
if (dataColorType == GrColorType::kRGB_888x) {
GrSurfaceDesc surfDesc;
surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
surfDesc.fWidth = width;
surfDesc.fHeight = height;
surfDesc.fConfig = kRGBA_8888_GrPixelConfig;
surfDesc.fSampleCnt = 1;
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
GrVkImage::ImageDesc imageDesc;
imageDesc.fImageType = VK_IMAGE_TYPE_2D;
imageDesc.fFormat = VK_FORMAT_R8G8B8A8_UNORM;
imageDesc.fWidth = width;
imageDesc.fHeight = height;
imageDesc.fLevels = 1;
imageDesc.fSamples = 1;
imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
imageDesc.fUsageFlags = usageFlags;
imageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
copyTexture = GrVkTexture::MakeNewTexture(this, SkBudgeted::kYes, surfDesc, imageDesc,
GrMipMapsStatus::kNotAllocated);
if (!copyTexture) {
return false;
}
uploadTexture = copyTexture.get();
uploadLeft = 0;
uploadTop = 0;
}
char* buffer = (char*) transferBuffer->map();
SkTArray<VkBufferImageCopy> regions(mipLevelCount);
currentWidth = width;
currentHeight = height;
int layerHeight = tex->height();
int layerHeight = uploadTexture->height();
for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
if (texelsShallowCopy[currentMipLevel].fPixels) {
SkASSERT(1 == mipLevelCount || currentHeight == layerHeight);
@ -663,7 +718,7 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
region.bufferRowLength = currentWidth;
region.bufferImageHeight = currentHeight;
region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(currentMipLevel), 0, 1 };
region.imageOffset = {left, top, 0};
region.imageOffset = {uploadLeft, uploadTop, 0};
region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 };
}
currentWidth = SkTMax(1, currentWidth/2);
@ -675,7 +730,7 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
transferBuffer->unmap();
// Change layout of our target so it can be copied to
tex->setImageLayout(this,
uploadTexture->setImageLayout(this,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
@ -684,11 +739,21 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, int left, int top, int widt
// Copy the buffer to the image
fCurrentCmdBuffer->copyBufferToImage(this,
transferBuffer,
tex,
uploadTexture,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
regions.count(),
regions.begin());
transferBuffer->unref();
// If we copied the data into a temporary image first, copy that image into our main texture
// now.
if (copyTexture.get()) {
SkASSERT(dataColorType == GrColorType::kRGB_888x);
static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
SkAssertResult(this->copySurface(tex, kOrigin, copyTexture.get(), kOrigin,
SkIRect::MakeWH(width, height), SkIPoint::Make(left, top),
false));
}
if (1 == mipLevelCount) {
tex->texturePriv().markMipMapsDirty();
}
@ -745,12 +810,11 @@ sk_sp<GrTexture> GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted
sk_sp<GrVkTexture> tex;
if (renderTarget) {
tex = GrVkTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted, desc,
tex = GrVkTextureRenderTarget::MakeNewTextureRenderTarget(this, budgeted, desc,
imageDesc,
mipMapsStatus);
} else {
tex = GrVkTexture::CreateNewTexture(this, budgeted, desc, imageDesc,
mipMapsStatus);
tex = GrVkTexture::MakeNewTexture(this, budgeted, desc, imageDesc, mipMapsStatus);
}
if (!tex) {
@ -1811,6 +1875,65 @@ bool GrVkGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int
return false;
}
// Skia's RGB_888x color type, which we map to the vulkan R8G8B8_UNORM, expects the data to be
// 32 bits, but the Vulkan format is only 24. So we first copy the surface into an R8G8B8A8
// image and then do the read pixels from that.
sk_sp<GrVkTextureRenderTarget> copySurface;
if (dstColorType == GrColorType::kRGB_888x) {
SkASSERT(image->imageFormat() == VK_FORMAT_R8G8B8_UNORM &&
surface->config() == kRGB_888_GrPixelConfig);
// Make a new surface that is RGBA to copy the RGB surface into.
GrSurfaceDesc surfDesc;
surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
surfDesc.fWidth = width;
surfDesc.fHeight = height;
surfDesc.fConfig = kRGBA_8888_GrPixelConfig;
surfDesc.fSampleCnt = 1;
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
GrVkImage::ImageDesc imageDesc;
imageDesc.fImageType = VK_IMAGE_TYPE_2D;
imageDesc.fFormat = VK_FORMAT_R8G8B8A8_UNORM;
imageDesc.fWidth = width;
imageDesc.fHeight = height;
imageDesc.fLevels = 1;
imageDesc.fSamples = 1;
imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
imageDesc.fUsageFlags = usageFlags;
imageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
copySurface = GrVkTextureRenderTarget::MakeNewTextureRenderTarget(
this, SkBudgeted::kYes, surfDesc, imageDesc, GrMipMapsStatus::kNotAllocated);
if (!copySurface) {
return false;
}
int srcSampleCount = 0;
if (rt) {
srcSampleCount = rt->numColorSamples();
}
static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
if (!this->vkCaps().canCopyAsBlit(copySurface->config(), 1, kOrigin,
surface->config(), srcSampleCount, kOrigin) ||
!this->vkCaps().canCopyAsDraw(copySurface->config(), false,
surface->config(), SkToBool(surface->asTexture()))) {
return false;
}
SkIRect srcRect = SkIRect::MakeXYWH(left, top, width, height);
if (!this->copySurface(copySurface.get(), kOrigin, surface, kOrigin,
srcRect, SkIPoint::Make(0,0))) {
return false;
}
top = 0;
left = 0;
dstColorType = GrColorType::kRGBA_8888;
image = copySurface.get();
}
// Change layout of our target so it can be used as copy
image->setImageLayout(this,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,

View File

@ -64,7 +64,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu,
SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
}
sk_sp<GrVkTexture> GrVkTexture::CreateNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
sk_sp<GrVkTexture> GrVkTexture::MakeNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
const GrSurfaceDesc& desc,
const GrVkImage::ImageDesc& imageDesc,
GrMipMapsStatus mipMapsStatus) {

View File

@ -17,7 +17,7 @@ struct GrVkImageInfo;
class GrVkTexture : public GrTexture, public virtual GrVkImage {
public:
static sk_sp<GrVkTexture> CreateNewTexture(GrVkGpu*,
static sk_sp<GrVkTexture> MakeNewTexture(GrVkGpu*,
SkBudgeted budgeted,
const GrSurfaceDesc&,
const GrVkImage::ImageDesc&,

View File

@ -200,7 +200,7 @@ sk_sp<GrVkTextureRenderTarget> GrVkTextureRenderTarget::Make(GrVkGpu* gpu,
}
sk_sp<GrVkTextureRenderTarget>
GrVkTextureRenderTarget::CreateNewTextureRenderTarget(GrVkGpu* gpu,
GrVkTextureRenderTarget::MakeNewTextureRenderTarget(GrVkGpu* gpu,
SkBudgeted budgeted,
const GrSurfaceDesc& desc,
const GrVkImage::ImageDesc& imageDesc,

View File

@ -25,7 +25,7 @@ struct GrVkImageInfo;
class GrVkTextureRenderTarget: public GrVkTexture, public GrVkRenderTarget {
public:
static sk_sp<GrVkTextureRenderTarget> CreateNewTextureRenderTarget(GrVkGpu*, SkBudgeted,
static sk_sp<GrVkTextureRenderTarget> MakeNewTextureRenderTarget(GrVkGpu*, SkBudgeted,
const GrSurfaceDesc&,
const GrVkImage::ImageDesc&,
GrMipMapsStatus);

View File

@ -23,8 +23,8 @@ bool GrPixelConfigToVkFormat(GrPixelConfig config, VkFormat* format) {
*format = VK_FORMAT_R8G8B8A8_UNORM;
return true;
case kRGB_888_GrPixelConfig:
// TODO: VK_FORMAT_R8G8B8_UNORM
return false;
*format = VK_FORMAT_R8G8B8_UNORM;
return true;
case kBGRA_8888_GrPixelConfig:
*format = VK_FORMAT_B8G8R8A8_UNORM;
return true;
@ -85,6 +85,8 @@ bool GrVkFormatPixelConfigPairIsValid(VkFormat format, GrPixelConfig config) {
return kSRGBA_8888_GrPixelConfig == config;
case VK_FORMAT_B8G8R8A8_SRGB:
return kSBGRA_8888_GrPixelConfig == config;
case VK_FORMAT_R8G8B8_UNORM:
return kRGB_888_GrPixelConfig == config;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return kRGBA_1010102_GrPixelConfig == config;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
@ -119,6 +121,7 @@ bool GrVkFormatIsSupported(VkFormat format) {
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: