From ac09f7cd7a283292e81d57e94936dfd3cc8ca018 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Fri, 8 May 2020 09:23:52 -0400 Subject: [PATCH] Add api on GrContext to update the data of GrBackendTextures. Change-Id: I680f12bf58fc7b66a6b2f3fa4c4723ae84d3f949 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/288555 Reviewed-by: Brian Salomon Commit-Queue: Greg Daniel --- RELEASE_NOTES.txt | 4 ++ include/gpu/GrContext.h | 36 +++++++++++ src/gpu/GrContext.cpp | 52 +++++++++++++++ tests/BackendAllocationTest.cpp | 108 +++++++++++++++++++++++--------- 4 files changed, 171 insertions(+), 29 deletions(-) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 5376549ea8ffe..3ffc104214128 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -6,6 +6,10 @@ This file includes a list of high level updates for each milestone release. Milestone 84 + * Add api on GrContext, updateBackendTexture that will upload new data to a + GrBackendTexture. + https://review.skia.org/288555 + * GrContext::createBackendTexture functions that initialize the texture no longer guarantee that all the data has been uploaded and the gpu is done with the texture. Instead the client can assume the upload work has been submitted to the gpu and they diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index bc664ee97408c..ae122bcc92d01 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -546,6 +546,42 @@ class SK_API GrContext : public GrRecordingContext { finishedContext); } + /** + * If possible, updates a backend texture to be filled to a particular color. The client should + * check the return value to see if the update was successful. The client can pass in a + * finishedProc to be notified when the data has been uploaded by the gpu and the texture can be + * deleted. The client can assume the upload work has been submitted to the gpu. The + * finishedProc will always get called even if we failed to update the GrBackendTexture. + * For the Vulkan backend after a successful update the layout of the created VkImage will be: + * VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + */ + bool updateBackendTexture(const GrBackendTexture&, + const SkColor4f& color, + GrGpuFinishedProc finishedProc, + GrGpuFinishedContext finishedContext); + + /** + * If possible, updates a backend texture filled with the provided pixmap data. The client + * should check the return value to see if the update was successful. The client can pass in a + * finishedProc to be notified when the data has been uploaded by the gpu and the texture can be + * deleted. The client can assume the upload work has been submitted to the gpu. The + * finishedProc will always get called even if we failed to create the GrBackendTexture. + * The backend texture must be compatible with the provided pixmap(s). Compatible, in this case, + * means that the backend format is compatible with the base pixmap's colortype. + * If the backend texture is mip mapped, the data for all the mipmap levels must be provided. + * In the mipmapped case all the colortypes of the provided pixmaps must be the same. + * Additionally, all the miplevels must be sized correctly (please see + * SkMipMap::ComputeLevelSize and ComputeLevelCount). + * Note: the pixmap's alphatypes and colorspaces are ignored. + * For the Vulkan backend after a successful update the layout of the created VkImage will be: + * VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + */ + bool updateBackendTexture(const GrBackendTexture&, + const SkPixmap srcData[], + int numLevels, + GrGpuFinishedProc finishedProc, + GrGpuFinishedContext finishedContext); + /** * Retrieve the GrBackendFormat for a given SkImage::CompressionType. This is * guaranteed to match the backend format used by the following diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 379e149640b64..07dfe1f25a6df 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -623,6 +623,58 @@ GrBackendTexture GrContext::createBackendTexture(const SkPixmap srcData[], int n finishedContext, &data); } +bool GrContext::updateBackendTexture(const GrBackendTexture& backendTexture, + const SkColor4f& color, + GrGpuFinishedProc finishedProc, + GrGpuFinishedContext finishedContext) { + if (!this->asDirectContext()) { + finishedProc(finishedContext); + return false; + } + + if (this->abandoned()) { + finishedProc(finishedContext); + return false; + } + + GrGpu::BackendTextureData data(color); + return fGpu->updateBackendTexture(backendTexture, finishedProc, finishedContext, &data); +} + +bool GrContext::updateBackendTexture(const GrBackendTexture& backendTexture, + const SkPixmap srcData[], + int numLevels, + GrGpuFinishedProc finishedProc, + GrGpuFinishedContext finishedContext) { + if (!this->asDirectContext()) { + finishedProc(finishedContext); + return false; + } + + if (this->abandoned()) { + finishedProc(finishedContext); + return false; + } + + if (!srcData || numLevels <= 0) { + finishedProc(finishedContext); + return false; + } + + int numExpectedLevels = 1; + if (backendTexture.hasMipMaps()) { + numExpectedLevels = SkMipMap::ComputeLevelCount(backendTexture.width(), + backendTexture.height()) + 1; + } + if (numLevels != numExpectedLevels) { + finishedProc(finishedContext); + return false; + } + + GrGpu::BackendTextureData data(srcData); + return fGpu->updateBackendTexture(backendTexture, finishedProc, finishedContext, &data); +} + ////////////////////////////////////////////////////////////////////////////// GrBackendTexture GrContext::createCompressedBackendTexture(int width, int height, diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp index fb7a21195fd0d..443f39b221b62 100644 --- a/tests/BackendAllocationTest.cpp +++ b/tests/BackendAllocationTest.cpp @@ -28,8 +28,7 @@ #include "src/gpu/mtl/GrMtlCppUtil.h" #endif -static void delete_backend_texture(GrContext* context, const GrBackendTexture& backendTexture, - bool* finishedCreate) { +static void wait_on_backend_work_to_finish(GrContext* context, bool* finishedCreate) { while (finishedCreate && !(*finishedCreate)) { context->checkAsyncWorkCompletion(); } @@ -39,9 +38,18 @@ static void delete_backend_texture(GrContext* context, const GrBackendTexture& b // Reset it here so that it can be use to signal a future backend texture's creation *finishedCreate = false; } +} + +static void delete_backend_texture(GrContext* context, const GrBackendTexture& backendTexture, + bool* finishedCreate) { + wait_on_backend_work_to_finish(context, finishedCreate); context->deleteBackendTexture(backendTexture); } +static void mark_signaled(void* context) { + *(bool*)context = true; +} + // Test wrapping of GrBackendObjects in SkSurfaces and SkImages (non-static since used in Mtl test) void test_wrapping(GrContext* context, skiatest::Reporter* reporter, std::functionpriv().caps()->getWriteSwizzle( + backendTex.getBackendFormat(), grColorType).applyTo(newColor); + context->updateBackendTexture(backendTex, swizzledColor, mark_signaled, finishedBECreate); + + checkBackendTexture(newColor); - // The last step in this test will dirty the mipmaps so do it last - check_base_readbacks(context, backendTex, skColorType, renderable, color, - reporter, "colorinit"); delete_backend_texture(context, backendTex, finishedBECreate); } @@ -461,22 +488,49 @@ static void test_pixmap_init(GrContext* context, skiatest::Reporter* reporter, return; } - if (mipMapped == GrMipMapped::kYes) { - SkColor4f expectedColors[6] = { - get_expected_color(colors[0], skColorType), - get_expected_color(colors[1], skColorType), - get_expected_color(colors[2], skColorType), - get_expected_color(colors[3], skColorType), - get_expected_color(colors[4], skColorType), - get_expected_color(colors[5], skColorType), - }; - - check_mipmaps(context, backendTex, skColorType, expectedColors, reporter, "pixmap"); + auto checkBackendTexture = [&](SkColor4f colors[6]) { + if (mipMapped == GrMipMapped::kYes) { + SkColor4f expectedColors[6] = { + get_expected_color(colors[0], skColorType), + get_expected_color(colors[1], skColorType), + get_expected_color(colors[2], skColorType), + get_expected_color(colors[3], skColorType), + get_expected_color(colors[4], skColorType), + get_expected_color(colors[5], skColorType), + }; + + check_mipmaps(context, backendTex, skColorType, expectedColors, reporter, "pixmap"); + } + + // The last step in this test will dirty the mipmaps so do it last + check_base_readbacks(context, backendTex, skColorType, renderable, colors[0], reporter, + "pixmap"); + }; + + checkBackendTexture(colors); + + // Make sure the initial create work has finished so we can test the update independently. + wait_on_backend_work_to_finish(context, finishedBECreate); + + SkColor4f colorsNew[6] = { + {1.0f, 1.0f, 0.0f, 0.2f}, // Y + {1.0f, 0.0f, 0.0f, 1.0f}, // R + {0.0f, 1.0f, 0.0f, 0.9f}, // G + {0.0f, 0.0f, 1.0f, 0.7f}, // B + {0.0f, 1.0f, 1.0f, 0.5f}, // C + {1.0f, 0.0f, 1.0f, 0.3f}, // M + }; + make_pixmaps(skColorType, mipMapped, colorsNew, pixmapMem); + for (int i = 0; i < numMipLevels; ++i) { + pixmaps[i].reset(pixmapMem[i].info(), pixmapMem[i].addr(), pixmapMem[i].rowBytes()); } - // The last step in this test will dirty the mipmaps so do it last - check_base_readbacks(context, backendTex, skColorType, renderable, colors[0], - reporter, "pixmap"); + // Upload new data and make sure everything still works + context->updateBackendTexture(backendTex, pixmaps, numMipLevels, mark_signaled, + finishedBECreate); + + checkBackendTexture(colorsNew); + delete_backend_texture(context, backendTex, finishedBECreate); } @@ -509,10 +563,6 @@ void check_vk_layout(const GrBackendTexture& backendTex, VkLayout layout) { #endif } -static void mark_signaled(void* context) { - *(bool*)context = true; -} - /////////////////////////////////////////////////////////////////////////////// // This test is a bit different from the others in this file. It is mainly checking that, for any // SkSurface we can create in Ganesh, we can also create a backend texture that is compatible with