implemented metal gpu backend texture upload testing

Bug: skia:
Change-Id: Ia3af58a0710f7f9792b37682a3cc45dd14282b71
Reviewed-on: https://skia-review.googlesource.com/140248
Commit-Queue: Timothy Liang <timliang@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Timothy Liang 2018-07-13 13:24:05 -04:00 committed by Skia Commit-Bot
parent 81d151103f
commit 36848f6b30
11 changed files with 321 additions and 60 deletions

View File

@ -19,6 +19,9 @@ bool IsGLContextType(sk_gpu_test::GrContextFactory::ContextType type) {
bool IsVulkanContextType(sk_gpu_test::GrContextFactory::ContextType type) {
return kVulkan_GrBackend == GrContextFactory::ContextTypeBackend(type);
}
bool IsMetalContextType(sk_gpu_test::GrContextFactory::ContextType type) {
return kMetal_GrBackend == GrContextFactory::ContextTypeBackend(type);
}
bool IsRenderingGLContextType(sk_gpu_test::GrContextFactory::ContextType type) {
return IsGLContextType(type) && GrContextFactory::IsRenderingContext(type);
}

View File

@ -102,6 +102,7 @@ tests_sources = [
"$_tests/GrShapeTest.cpp",
"$_tests/GrSKSLPrettyPrintTest.cpp",
"$_tests/GrSurfaceTest.cpp",
"$_tests/GrTestingBackendTextureUploadTest.cpp",
"$_tests/GrTextureMipMapInvalidationTest.cpp",
"$_tests/GrTRecorderTest.cpp",
"$_tests/HashTest.cpp",

View File

@ -39,6 +39,23 @@ public:
kSkip_SyncQueue
};
#ifdef GR_TEST_UTILS
GrBackendTexture createTestingOnlyBackendTexture(const void* pixels, int w, int h,
GrPixelConfig config, bool isRT,
GrMipMapped) override;
bool isTestingOnlyBackendTexture(const GrBackendTexture&) const override;
void deleteTestingOnlyBackendTexture(const GrBackendTexture&) override;
GrBackendRenderTarget createTestingOnlyBackendRenderTarget(int w, int h, GrColorType,
GrSRGBEncoded) override;
void deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget&) override;
void testingOnly_flushGpuAndSync() override;
#endif
bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
GrSurface* src, GrSurfaceOrigin srcOrigin,
const SkIRect& srcRect,
@ -135,21 +152,9 @@ private:
void clearStencil(GrRenderTarget* target, int clearValue) override {}
#if GR_TEST_UTILS
GrBackendTexture createTestingOnlyBackendTexture(const void* pixels, int w, int h,
GrPixelConfig config, bool isRT,
GrMipMapped) override {
return GrBackendTexture();
}
bool isTestingOnlyBackendTexture(const GrBackendTexture&) const override { return false; }
void deleteTestingOnlyBackendTexture(const GrBackendTexture&) override {}
GrBackendRenderTarget createTestingOnlyBackendRenderTarget(int w, int h, GrColorType,
GrSRGBEncoded) override {
return {};
}
void deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget&) override {}
void testingOnly_flushGpuAndSync() override {}
bool createTestingOnlyMtlTextureInfo(GrPixelConfig config, int w, int h, bool texturable,
bool renderable, GrMipMapped mipMapped,
const void* srcData, GrMtlTextureInfo* info);
#endif
sk_sp<GrMtlCaps> fMtlCaps;

View File

@ -95,21 +95,6 @@ GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
fCaps = fMtlCaps;
fCmdBuffer = [fQueue commandBuffer];
MTLTextureDescriptor* txDesc = [[MTLTextureDescriptor alloc] init];
txDesc.textureType = MTLTextureType3D;
txDesc.height = 64;
txDesc.width = 64;
txDesc.depth = 64;
txDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;
txDesc.arrayLength = 1;
txDesc.mipmapLevelCount = 1;
id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor:txDesc];
// To get ride of unused var warning
int width = [testTexture width];
SkDebugf("width: %d\n", width);
// Unused queue warning fix
SkDebugf("ptr to queue: %p\n", fQueue);
}
void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
@ -364,6 +349,171 @@ sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
}
#ifdef GR_TEST_UTILS
bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrPixelConfig config, int w, int h, bool texturable,
bool renderable, GrMipMapped mipMapped,
const void* srcData, GrMtlTextureInfo* info) {
SkASSERT(texturable || renderable);
if (!texturable) {
SkASSERT(GrMipMapped::kNo == mipMapped);
SkASSERT(!srcData);
}
MTLPixelFormat format;
if (!GrPixelConfigToMTLFormat(config, &format)) {
return false;
}
if (texturable && !fMtlCaps->isConfigTexturable(config)) {
return false;
}
if (renderable && !fMtlCaps->isConfigRenderable(config)) {
return false;
}
// Currently we don't support uploading pixel data when mipped.
if (srcData && GrMipMapped::kYes == mipMapped) {
return false;
}
if(!check_max_blit_width(w)) {
return false;
}
bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
MTLTextureDescriptor* desc =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
width: w
height: h
mipmapped: mipmapped];
desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
desc.storageMode = MTLStorageModePrivate;
desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
SkAutoTMalloc<GrColor> srcBuffer;
if (!srcData) {
srcBuffer.reset(w * h);
memset(srcBuffer, 0, w * h * sizeof(GrColor));
srcData = srcBuffer;
}
SkASSERT(srcData);
desc.storageMode = MTLStorageModeManaged;
id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor: desc];
auto colorType = GrPixelConfigToColorType(config);
int rowBytes = w * GrColorTypeBytesPerPixel(colorType);
MTLOrigin origin = MTLOriginMake(0, 0, 0);
SkASSERT(testTexture.pixelFormat == transferTexture.pixelFormat);
SkASSERT(testTexture.sampleCount == transferTexture.sampleCount);
id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
int currentWidth = w;
int currentHeight = h;
for (int mipLevel = 0; mipLevel < (int)testTexture.mipmapLevelCount; mipLevel++) {
[transferTexture replaceRegion: MTLRegionMake2D(0, 0, currentWidth, currentHeight)
mipmapLevel: mipLevel
withBytes: srcData
bytesPerRow: rowBytes];
[blitCmdEncoder copyFromTexture: transferTexture
sourceSlice: 0
sourceLevel: mipLevel
sourceOrigin: origin
sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
toTexture: testTexture
destinationSlice: 0
destinationLevel: mipLevel
destinationOrigin: origin];
currentWidth = SkTMax(1, currentWidth/2);
currentHeight = SkTMax(1, currentHeight/2);
}
[blitCmdEncoder endEncoding];
[cmdBuffer commit];
[cmdBuffer waitUntilCompleted];
info->fTexture = GrReleaseId(testTexture);
return true;
}
GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h,
GrPixelConfig config, bool isRT,
GrMipMapped mipMapped) {
if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
return GrBackendTexture();
}
GrMtlTextureInfo info;
if (!this->createTestingOnlyMtlTextureInfo(config, w, h, true, isRT, mipMapped, pixels,
&info)) {
return {};
}
GrBackendTexture backendTex(w, h, mipMapped, info);
backendTex.fConfig = config;
return backendTex;
}
bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
SkASSERT(kMetal_GrBackend == tex.backend());
GrMtlTextureInfo info;
if (!tex.getMtlTextureInfo(&info)) {
return false;
}
id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
GrWrapOwnership::kBorrow_GrWrapOwnership);
if (!mtlTexture) {
return false;
}
return mtlTexture.usage & MTLTextureUsageShaderRead;
}
void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
SkASSERT(kMetal_GrBackend == tex.fBackend);
GrMtlTextureInfo info;
if (tex.getMtlTextureInfo(&info)) {
// Adopts the metal texture so that ARC will clean it up.
GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
}
}
GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct,
GrSRGBEncoded srgbEncoded) {
if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
return GrBackendRenderTarget();
}
auto config = GrColorTypeToPixelConfig(ct, srgbEncoded);
if (kUnknown_GrPixelConfig == config) {
return {};
}
GrMtlTextureInfo info;
if (!this->createTestingOnlyMtlTextureInfo(config, w, h, false, true, GrMipMapped::kNo, nullptr,
&info)) {
return {};
}
GrBackendRenderTarget backendRT(w, h, 1, info);
backendRT.fConfig = config;
return backendRT;
}
void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
SkASSERT(kMetal_GrBackend == rt.fBackend);
GrMtlTextureInfo info;
if (rt.getMtlTextureInfo(&info)) {
this->testingOnly_flushGpuAndSync();
// Adopts the metal texture so that ARC will clean it up.
GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
}
}
void GrMtlGpu::testingOnly_flushGpuAndSync() {
this->submitCommandBuffer(kForce_SyncQueue);
}
#endif // GR_TEST_UTILS
bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType dstColorType, void* buffer, size_t rowBytes) {
SkASSERT(surface);

View File

@ -34,6 +34,11 @@ id<MTLTexture> GrGetMTLTexture(const void* mtlTexture, GrWrapOwnership);
*/
const void* GrGetPtrFromId(id idObject);
/**
* Returns a const void* to whatever the id object is pointing to. Always uses __bridge_retained.
*/
const void* GrReleaseId(id idObject);
/**
* Returns a MTLTextureDescriptor which describes the MTLTexture. Useful when creating a duplicate
* MTLTexture without the same storage allocation.

View File

@ -126,6 +126,10 @@ const void* GrGetPtrFromId(id idObject) {
return (__bridge const void*)idObject;
}
const void* GrReleaseId(id idObject) {
return (__bridge_retained const void*)idObject;
}
MTLTextureDescriptor* GrGetMTLTextureDescriptor(id<MTLTexture> mtlTexture) {
MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
texDesc.textureType = mtlTexture.textureType;

View File

@ -0,0 +1,81 @@
/*
* Copyright 2018 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"
#include "GrGpu.h"
#include "GrContextPriv.h"
#include "GrTexture.h"
#include "SkConvertPixels.h"
#include "Test.h"
#include "TestUtils.h"
void testing_only_texture_test(skiatest::Reporter* reporter, GrContext* context, GrColorType ct,
bool renderTarget, bool doDataUpload, GrMipMapped mipMapped) {
const int kWidth = 16;
const int kHeight = 16;
SkAutoTMalloc<GrColor> srcBuffer;
if (doDataUpload) {
srcBuffer.reset(kWidth * kHeight);
fill_pixel_data(kWidth, kHeight, srcBuffer.get());
}
SkAutoTMalloc<GrColor> dstBuffer(kWidth * kHeight);
GrGpu* gpu = context->contextPriv().getGpu();
GrPixelConfig config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
if (!gpu->caps()->isConfigTexturable(config)) {
return;
}
GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(srcBuffer,
kWidth,
kHeight,
config,
renderTarget,
mipMapped);
sk_sp<GrTexture> wrappedTex;
if (renderTarget) {
wrappedTex = gpu->wrapRenderableBackendTexture(backendTex, 1,
GrWrapOwnership::kAdopt_GrWrapOwnership);
} else {
wrappedTex = gpu->wrapBackendTexture(backendTex,
GrWrapOwnership::kAdopt_GrWrapOwnership);
}
REPORTER_ASSERT(reporter, wrappedTex);
int rowBytes = GrColorTypeBytesPerPixel(ct) * kWidth;
bool result = gpu->readPixels(wrappedTex.get(), 0, 0, kWidth,
kHeight, ct, dstBuffer, rowBytes);
if (!doDataUpload) {
// createTestingOnlyBackendTexture will fill the texture with 0's if no data is provided, so
// we set the expected result likewise.
srcBuffer.reset(kWidth * kHeight);
memset(srcBuffer, 0, kWidth * kHeight * sizeof(GrColor));
}
REPORTER_ASSERT(reporter, result);
REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer, dstBuffer,
kWidth, kHeight));
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrTestingBackendTextureUploadTest, reporter, ctxInfo) {
for (auto colorType: {GrColorType::kRGBA_8888, GrColorType::kBGRA_8888}) {
for (bool renderable: {true, false}) {
for (bool doDataUpload: {true, false}) {
testing_only_texture_test(reporter, ctxInfo.grContext(), colorType,
renderable, doDataUpload, GrMipMapped::kNo);
if (!doDataUpload) {
testing_only_texture_test(reporter, ctxInfo.grContext(), colorType,
renderable, doDataUpload, GrMipMapped::kYes);
}
}
}
}
}

View File

@ -129,6 +129,7 @@ typedef bool GrContextTypeFilterFn(GrContextFactoryContextType);
extern bool IsGLContextType(GrContextFactoryContextType);
extern bool IsVulkanContextType(GrContextFactoryContextType);
extern bool IsMetalContextType(GrContextFactoryContextType);
extern bool IsRenderingGLContextType(GrContextFactoryContextType);
extern bool IsNullGLContextType(GrContextFactoryContextType);
void RunWithGPUTestContexts(GrContextTestFn*, GrContextTypeFilterFn*, Reporter*,
@ -213,6 +214,9 @@ private:
#define DEF_GPUTEST_FOR_VULKAN_CONTEXT(name, reporter, context_info) \
DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsVulkanContextType, \
reporter, context_info, nullptr)
#define DEF_GPUTEST_FOR_METAL_CONTEXT(name, reporter, context_info) \
DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsMetalContextType, \
reporter, context_info, nullptr)
#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER) \
do { \

View File

@ -113,3 +113,32 @@ void test_copy_to_surface(skiatest::Reporter* reporter, GrProxyProvider* proxyPr
}
}
}
void fill_pixel_data(int width, int height, GrColor* data) {
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
unsigned int red = (unsigned int)(256.f * (i / (float)width));
unsigned int green = (unsigned int)(256.f * (j / (float)height));
data[i + j * width] = GrColorPackRGBA(red - (red >> 8), green - (green >> 8),
0xff, 0xff);
}
}
}
bool does_full_buffer_contain_correct_color(GrColor* srcBuffer,
GrColor* dstBuffer,
int width,
int height) {
GrColor* srcPtr = srcBuffer;
GrColor* dstPtr = dstBuffer;
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
if (srcPtr[i] != dstPtr[i]) {
return false;
}
}
srcPtr += width;
dstPtr += width;
}
return true;
}

View File

@ -9,6 +9,7 @@
class GrSurfaceContext;
class GrSurfaceProxy;
typedef uint32_t GrColor;
// Ensure that reading back from 'srcContext' as RGBA 8888 matches 'expectedPixelValues
void test_read_pixels(skiatest::Reporter*,
@ -29,3 +30,10 @@ void test_copy_from_surface(skiatest::Reporter*, GrContext*,
// Ensure that RGBA 8888 pixels can be copied into 'dstContext'
void test_copy_to_surface(skiatest::Reporter*, GrProxyProvider*,
GrSurfaceContext* dstContext, const char* testName);
// Fills data with a red-green gradient
void fill_pixel_data(int width, int height, GrColor* data);
// Checks srcBuffer and dstBuffer contain the same colors
bool does_full_buffer_contain_correct_color(GrColor* srcBuffer, GrColor* dstBuffer, int width,
int height);

View File

@ -18,40 +18,11 @@
#include "ProxyUtils.h"
#include "SkGr.h"
#include "Test.h"
#include "TestUtils.h"
#include "vk/GrVkGpu.h"
using sk_gpu_test::GrContextFactory;
void fill_pixel_data(int width, int height, GrColor* data) {
// build red-green gradient
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
unsigned int red = (unsigned int)(256.f*(i / (float)width));
unsigned int green = (unsigned int)(256.f*(j / (float)height));
data[i + j*width] = GrColorPackRGBA(red - (red>>8), green - (green>>8), 0xff, 0xff);
}
}
}
bool does_full_buffer_contain_correct_color(GrColor* srcBuffer,
GrColor* dstBuffer,
int width,
int height) {
GrColor* srcPtr = srcBuffer;
GrColor* dstPtr = dstBuffer;
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
if (srcPtr[i] != dstPtr[i]) {
return false;
}
}
srcPtr += width;
dstPtr += width;
}
return true;
}
void basic_texture_test(skiatest::Reporter* reporter, GrContext* context, SkColorType ct,
bool renderTarget) {
const int kWidth = 16;