Reland "Add SkSurface resolve function."
This is a reland of d921f21fbc
Original change's description:
> Add SkSurface resolve function.
>
> This will insert a resolve msaa call into the stream of commands for
> the SkSurface. This is mostly useful for cases when a client wraps the
> resolve texture but has Skia draw with MSAA, and the client wants to
> make sure Skia resolves to their wrapped texture.
>
> Bug: chromium:1292418
> Change-Id: I6eddae967136716b9215fcd96e7d77a2457efdf2
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/503340
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Commit-Queue: Greg Daniel <egdaniel@google.com>
Bug: chromium:1292418
Change-Id: I810b5618092c560f5bba900024b3b8c0c88baea9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/503717
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
This commit is contained in:
parent
005392cbc4
commit
2a4a0b7f1f
@ -7,6 +7,8 @@ Milestone 100
|
||||
* Skia now requires C++17 and the corresponding standard library (or newer).
|
||||
* Skia on iOS now requires iOS 11 to build; earlier versions of iOS do not support C++17.
|
||||
* The skstd::string_view class has been replaced with the C++17 native std::string_view.
|
||||
* Added SkSurface::resolveMSAA api to force Skia to resolve MSAA draws. Useful for when
|
||||
Skia wraps a clients texture as the resolve target.
|
||||
|
||||
* * *
|
||||
|
||||
|
@ -107,6 +107,7 @@ tests_sources = [
|
||||
"$_tests/GrSlugTest.cpp",
|
||||
"$_tests/GrStyledShapeTest.cpp",
|
||||
"$_tests/GrSubmittedFlushTest.cpp",
|
||||
"$_tests/GrSurfaceResolveTest.cpp",
|
||||
"$_tests/GrSurfaceTest.cpp",
|
||||
"$_tests/GrTextBlobTest.cpp",
|
||||
"$_tests/GrTextureMipMapInvalidationTest.cpp",
|
||||
|
@ -910,6 +910,19 @@ public:
|
||||
};
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
/** If a surface is GPU texture backed, is being drawn with MSAA, and there is a resolve
|
||||
texture, this call will insert a resolve command into the stream of gpu commands. In order
|
||||
for the resolve to actually have an effect, the work still needs to be flushed and submitted
|
||||
to the GPU after recording the resolve command. If a resolve is not supported or the
|
||||
SkSurface has no dirty work to resolve, then this call is a no-op.
|
||||
|
||||
This call is most useful when the SkSurface is created by wrapping a single sampled gpu
|
||||
texture, but asking Skia to render with MSAA. If the client wants to use the wrapped texture
|
||||
outside of Skia, the only way to trigger a resolve is either to call this command or use
|
||||
SkSurface::flush.
|
||||
*/
|
||||
void resolveMSAA();
|
||||
|
||||
/** Issues pending SkSurface commands to the GPU-backed API objects and resolves any SkSurface
|
||||
MSAA. A call to GrDirectContext::submit is always required to ensure work is actually sent
|
||||
to the gpu. Some specific API details:
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
const GrBackendSemaphore* waitSemaphores,
|
||||
bool deleteSemaphoresAfterWait) = 0;
|
||||
virtual void discard() = 0;
|
||||
virtual void resolveMSAA() = 0;
|
||||
|
||||
virtual bool replaceBackingProxy(SkSurface::ContentChangeMode,
|
||||
sk_sp<GrRenderTargetProxy>,
|
||||
|
@ -31,6 +31,7 @@ GrCaps::GrCaps(const GrContextOptions& options) {
|
||||
fConservativeRasterSupport = false;
|
||||
fWireframeSupport = false;
|
||||
fMSAAResolvesAutomatically = false;
|
||||
fPreferDiscardableMSAAAttachment = false;
|
||||
fUsePrimitiveRestart = false;
|
||||
fPreferClientSideDynamicBuffers = false;
|
||||
fPreferFullscreenClears = false;
|
||||
|
@ -75,6 +75,13 @@ public:
|
||||
// an MSAA-render-to-texture extension: Any render target we create internally will use the
|
||||
// extension, and any wrapped render target is the client's responsibility.
|
||||
bool msaaResolvesAutomatically() const { return fMSAAResolvesAutomatically; }
|
||||
// If true then when doing MSAA draws, we will prefer to discard the msaa attachment on load
|
||||
// and stores. The use of this feature for specific draws depends on the render target having a
|
||||
// resolve attachment, and if we need to load previous data the resolve attachment must be
|
||||
// usable as an input attachment/texture. Otherwise we will just write out and store the msaa
|
||||
// attachment like normal.
|
||||
// This flag is similar to enabling gl render to texture for msaa rendering.
|
||||
bool preferDiscardableMSAAAttachment() const { return fPreferDiscardableMSAAAttachment; }
|
||||
bool halfFloatVertexAttributeSupport() const { return fHalfFloatVertexAttributeSupport; }
|
||||
|
||||
// Primitive restart functionality is core in ES 3.0, but using it will cause slowdowns on some
|
||||
@ -544,6 +551,7 @@ protected:
|
||||
bool fConservativeRasterSupport : 1;
|
||||
bool fWireframeSupport : 1;
|
||||
bool fMSAAResolvesAutomatically : 1;
|
||||
bool fPreferDiscardableMSAAAttachment : 1;
|
||||
bool fUsePrimitiveRestart : 1;
|
||||
bool fPreferClientSideDynamicBuffers : 1;
|
||||
bool fPreferFullscreenClears : 1;
|
||||
|
@ -753,7 +753,8 @@ void GrDrawingManager::addAtlasTask(sk_sp<GrRenderTask> atlasTask,
|
||||
}
|
||||
#endif // SK_GPU_V1
|
||||
|
||||
GrTextureResolveRenderTask* GrDrawingManager::newTextureResolveRenderTask(const GrCaps& caps) {
|
||||
GrTextureResolveRenderTask* GrDrawingManager::newTextureResolveRenderTaskBefore(
|
||||
const GrCaps& caps) {
|
||||
// Unlike in the "new opsTask" case, we do not want to close the active opsTask, nor (if we are
|
||||
// in sorting and opsTask reduction mode) the render tasks that depend on any proxy's current
|
||||
// state. This is because those opsTasks can still receive new ops and because if they refer to
|
||||
@ -767,6 +768,34 @@ GrTextureResolveRenderTask* GrDrawingManager::newTextureResolveRenderTask(const
|
||||
return static_cast<GrTextureResolveRenderTask*>(task);
|
||||
}
|
||||
|
||||
void GrDrawingManager::newTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
|
||||
GrSurfaceProxy::ResolveFlags flags,
|
||||
const GrCaps& caps) {
|
||||
SkDEBUGCODE(this->validate());
|
||||
SkASSERT(fContext);
|
||||
|
||||
GrRenderTask* lastTask = this->getLastRenderTask(proxy.get());
|
||||
if ((!proxy->asRenderTargetProxy()->isMSAADirty() && (!lastTask || lastTask->isClosed())) ||
|
||||
caps.msaaResolvesAutomatically()) {
|
||||
SkDEBUGCODE(this->validate());
|
||||
return;
|
||||
}
|
||||
|
||||
this->closeActiveOpsTask();
|
||||
|
||||
auto resolveTask = sk_make_sp<GrTextureResolveRenderTask>();
|
||||
// Add proxy also adds all the needed dependencies we need
|
||||
resolveTask->addProxy(this, std::move(proxy), flags, caps);
|
||||
|
||||
auto task = this->appendTask(std::move(resolveTask));
|
||||
task->makeClosed(fContext);
|
||||
|
||||
// We have closed the previous active oplist but since a new oplist isn't being added there
|
||||
// shouldn't be an active one.
|
||||
SkASSERT(!fActiveOpsTask);
|
||||
SkDEBUGCODE(this->validate());
|
||||
}
|
||||
|
||||
void GrDrawingManager::newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy,
|
||||
std::unique_ptr<std::unique_ptr<GrSemaphore>[]> semaphores,
|
||||
int numSemaphores) {
|
||||
|
@ -63,9 +63,15 @@ public:
|
||||
#endif
|
||||
|
||||
// Create a render task that can resolve MSAA and/or regenerate mipmap levels on proxies. This
|
||||
// method will only add the new render task to the list. It is up to the caller to call
|
||||
// addProxy() on the returned object.
|
||||
GrTextureResolveRenderTask* newTextureResolveRenderTask(const GrCaps&);
|
||||
// method will only add the new render task to the list. However, it adds the task before the
|
||||
// last task in the list. It is up to the caller to call addProxy() on the returned object.
|
||||
GrTextureResolveRenderTask* newTextureResolveRenderTaskBefore(const GrCaps&);
|
||||
|
||||
// Creates a render task that can resolve MSAA and/or regenerate mimap levels on the passed in
|
||||
// proxy. The task is appended to the end of the current list of tasks.
|
||||
void newTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
|
||||
GrSurfaceProxy::ResolveFlags,
|
||||
const GrCaps&);
|
||||
|
||||
// Create a new render task that will cause the gpu to wait on semaphores before executing any
|
||||
// more RenderTasks that target proxy. It is possible for this wait to also block additional
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
|
||||
GrTextureResolveRenderTask* newTextureResolveRenderTask(const GrCaps& caps) const {
|
||||
SkASSERT(fDrawingManager);
|
||||
return fDrawingManager->newTextureResolveRenderTask(caps);
|
||||
return fDrawingManager->newTextureResolveRenderTaskBefore(caps);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -23,6 +23,8 @@ public:
|
||||
*/
|
||||
virtual void discard() = 0;
|
||||
|
||||
virtual void resolveMSAA() = 0;
|
||||
|
||||
/**
|
||||
* Clear the rect of the render target to the given color.
|
||||
* @param rect the rect to clear to
|
||||
|
@ -93,12 +93,6 @@ public:
|
||||
// if true, MTLStoreActionStoreAndMultiplesampleResolve is available
|
||||
bool storeAndMultisampleResolveSupport() const { return fStoreAndMultisampleResolveSupport; }
|
||||
|
||||
// If true when doing MSAA draws, we will prefer to discard the MSAA attachment on load
|
||||
// and stores. The use of this feature for specific draws depends on the render target having a
|
||||
// resolve attachment, and if we need to load previous data the resolve attachment must
|
||||
// be readable in a shader. Otherwise we will just write out and store the MSAA attachment
|
||||
// like normal.
|
||||
bool preferDiscardableMSAAAttachment() const { return fPreferDiscardableMSAAAttachment; }
|
||||
bool renderTargetSupportsDiscardableMSAA(const GrMtlRenderTarget*) const;
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
@ -200,7 +194,6 @@ private:
|
||||
MTLPixelFormat fPreferredStencilFormat;
|
||||
|
||||
bool fStoreAndMultisampleResolveSupport : 1;
|
||||
bool fPreferDiscardableMSAAAttachment : 1;
|
||||
|
||||
using INHERITED = GrCaps;
|
||||
};
|
||||
|
@ -38,6 +38,10 @@ public:
|
||||
fSurfaceDrawContext->discard();
|
||||
}
|
||||
|
||||
void resolveMSAA() override {
|
||||
fSurfaceDrawContext->resolveMSAA();
|
||||
}
|
||||
|
||||
bool replaceBackingProxy(SkSurface::ContentChangeMode,
|
||||
sk_sp<GrRenderTargetProxy>,
|
||||
GrColorType,
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/gpu/GrDstProxyView.h"
|
||||
#include "src/gpu/GrImageContextPriv.h"
|
||||
#include "src/gpu/GrProxyProvider.h"
|
||||
#include "src/gpu/GrTextureResolveRenderTask.h"
|
||||
#include "src/gpu/effects/GrTextureEffect.h"
|
||||
#include "src/gpu/geometry/GrRect.h"
|
||||
#include "src/gpu/ops/ClearOp.h"
|
||||
@ -160,6 +161,19 @@ void SurfaceFillContext::discard() {
|
||||
this->getOpsTask()->discard();
|
||||
}
|
||||
|
||||
void SurfaceFillContext::resolveMSAA() {
|
||||
ASSERT_SINGLE_OWNER
|
||||
RETURN_IF_ABANDONED
|
||||
SkDEBUGCODE(this->validate();)
|
||||
GR_CREATE_TRACE_MARKER_CONTEXT("v1::SurfaceFillContext", "resolveMSAA", fContext);
|
||||
|
||||
AutoCheckFlush acf(this->drawingManager());
|
||||
|
||||
this->drawingManager()->newTextureResolveRenderTask(this->asSurfaceProxyRef(),
|
||||
GrSurfaceProxy::ResolveFlags::kMSAA,
|
||||
*this->caps());
|
||||
}
|
||||
|
||||
void SurfaceFillContext::internalClear(const SkIRect* scissor,
|
||||
std::array<float, 4> color,
|
||||
bool upgradePartialToFull) {
|
||||
|
@ -38,6 +38,8 @@ public:
|
||||
|
||||
void discard() override;
|
||||
|
||||
void resolveMSAA() override;
|
||||
|
||||
void fillRectWithFP(const SkIRect& dstRect, std::unique_ptr<GrFragmentProcessor> fp) override;
|
||||
|
||||
bool blitTexture(GrSurfaceProxyView view,
|
||||
|
@ -253,13 +253,6 @@ public:
|
||||
|
||||
VkShaderStageFlags getPushConstantStageFlags() const;
|
||||
|
||||
// If true then when doing MSAA draws, we will prefer to discard the msaa attachment on load
|
||||
// and stores. The use of this feature for specific draws depends on the render target having a
|
||||
// resolve attachment, and if we need to load previous data the resolve attachment must be
|
||||
// usable as an input attachment. Otherwise we will just write out and store the msaa attachment
|
||||
// like normal.
|
||||
// This flag is similar to enabling gl render to texture for msaa rendering.
|
||||
bool preferDiscardableMSAAAttachment() const { return fPreferDiscardableMSAAAttachment; }
|
||||
bool mustLoadFullImageWithDiscardableMSAA() const {
|
||||
return fMustLoadFullImageWithDiscardableMSAA;
|
||||
}
|
||||
@ -418,7 +411,6 @@ private:
|
||||
|
||||
uint32_t fMaxInputAttachmentDescriptors = 0;
|
||||
|
||||
bool fPreferDiscardableMSAAAttachment = false;
|
||||
bool fMustLoadFullImageWithDiscardableMSAA = false;
|
||||
bool fSupportsDiscardableMSAAForDMSAA = true;
|
||||
bool fSupportsMemorylessAttachments = false;
|
||||
|
@ -356,6 +356,10 @@ bool SkSurface::replaceBackendTexture(const GrBackendTexture& backendTexture,
|
||||
releaseContext);
|
||||
}
|
||||
|
||||
void SkSurface::resolveMSAA() {
|
||||
asSB(this)->onResolveMSAA();
|
||||
}
|
||||
|
||||
GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, const GrFlushInfo& flushInfo) {
|
||||
return asSB(this)->onFlush(access, flushInfo, nullptr);
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ public:
|
||||
TextureReleaseProc,
|
||||
ReleaseContext);
|
||||
|
||||
virtual void onResolveMSAA() {}
|
||||
|
||||
/**
|
||||
* Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
|
||||
* Inserts the requested number of semaphores for the gpu to signal when work is complete on the
|
||||
|
@ -208,6 +208,8 @@ bool SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
|
||||
|
||||
void SkSurface_Gpu::onDiscard() { fDevice->discard(); }
|
||||
|
||||
void SkSurface_Gpu::onResolveMSAA() { fDevice->resolveMSAA(); }
|
||||
|
||||
GrSemaphoresSubmitted SkSurface_Gpu::onFlush(BackendSurfaceAccess access, const GrFlushInfo& info,
|
||||
const GrBackendSurfaceMutableState* newState) {
|
||||
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
ReadPixelsContext context) override;
|
||||
bool onCopyOnWrite(ContentChangeMode) override;
|
||||
void onDiscard() override;
|
||||
void onResolveMSAA() override;
|
||||
GrSemaphoresSubmitted onFlush(BackendSurfaceAccess access, const GrFlushInfo& info,
|
||||
const GrBackendSurfaceMutableState*) override;
|
||||
bool onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores,
|
||||
|
156
tests/GrSurfaceResolveTest.cpp
Normal file
156
tests/GrSurfaceResolveTest.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "tests/Test.h"
|
||||
|
||||
#include "include/core/SkBitmap.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include "src/gpu/GrDirectContextPriv.h"
|
||||
#include "src/gpu/GrPixmap.h"
|
||||
#include "tests/TestUtils.h"
|
||||
#include "tools/gpu/ManagedBackendTexture.h"
|
||||
|
||||
using namespace sk_gpu_test;
|
||||
|
||||
bool check_pixels(skiatest::Reporter* reporter,
|
||||
GrDirectContext* dContext,
|
||||
const GrBackendTexture& tex,
|
||||
const SkImageInfo& info,
|
||||
SkColor expectedColor) {
|
||||
// We have to do the readback of the backend texture wrapped in a different Skia surface than
|
||||
// the one used in the main body of the test or else the readPixels call will trigger resolves
|
||||
// itself.
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
|
||||
tex,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
/*sampleCnt=*/4,
|
||||
kRGBA_8888_SkColorType,
|
||||
nullptr, nullptr);
|
||||
SkBitmap actual;
|
||||
actual.allocPixels(info);
|
||||
if (!surface->readPixels(actual, 0, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkBitmap expected;
|
||||
expected.allocPixels(info);
|
||||
SkCanvas tmp(expected);
|
||||
tmp.clear(expectedColor);
|
||||
expected.setImmutable();
|
||||
|
||||
const float tols[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
auto error = std::function<ComparePixmapsErrorReporter>(
|
||||
[reporter](int x, int y, const float diffs[4]) {
|
||||
SkASSERT(x >= 0 && y >= 0);
|
||||
ERRORF(reporter, "mismatch at %d, %d (%f, %f, %f %f)",
|
||||
x, y, diffs[0], diffs[1], diffs[2], diffs[3]);
|
||||
});
|
||||
|
||||
return ComparePixels(expected.pixmap(), actual.pixmap(), tols, error);
|
||||
}
|
||||
|
||||
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest, reporter, ctxInfo) {
|
||||
auto dContext = ctxInfo.directContext();
|
||||
|
||||
SkImageInfo info = SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
||||
|
||||
auto managedTex = ManagedBackendTexture::MakeFromInfo(dContext,
|
||||
info,
|
||||
GrMipmapped::kNo,
|
||||
GrRenderable::kYes);
|
||||
if (!managedTex) {
|
||||
return;
|
||||
}
|
||||
auto tex = managedTex->texture();
|
||||
// Wrap the backend surface but tell it rendering with MSAA so that the wrapped texture is the
|
||||
// resolve.
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
|
||||
tex,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
/*sampleCnt=*/4,
|
||||
kRGBA_8888_SkColorType,
|
||||
nullptr, nullptr);
|
||||
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GrCaps* caps = dContext->priv().caps();
|
||||
// In metal and vulkan if we prefer discardable msaa attachments we will also auto resolve. The
|
||||
// GrBackendTexture and SkSurface are set up in a way that is compatible with discardable msaa
|
||||
// for both backends.
|
||||
bool autoResolves = caps->msaaResolvesAutomatically() ||
|
||||
caps->preferDiscardableMSAAAttachment();
|
||||
|
||||
// First do a simple test where we clear the surface than flush with SkSurface::flush. This
|
||||
// should trigger the resolve and the texture should have the correct data.
|
||||
surface->getCanvas()->clear(SK_ColorRED);
|
||||
surface->flush();
|
||||
dContext->submit();
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
|
||||
|
||||
// Next try doing a GrDirectContext::flush which will not trigger a resolve on gpus without
|
||||
// automatic msaa resolves.
|
||||
surface->getCanvas()->clear(SK_ColorBLUE);
|
||||
dContext->flush();
|
||||
dContext->submit();
|
||||
if (autoResolves) {
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
|
||||
} else {
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
|
||||
}
|
||||
|
||||
// Now doing a surface flush (even without any queued up normal work) should still resolve the
|
||||
// surface.
|
||||
surface->flush();
|
||||
dContext->submit();
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
|
||||
|
||||
// Test using SkSurface::resolve with a GrDirectContext::flush
|
||||
surface->getCanvas()->clear(SK_ColorRED);
|
||||
surface->resolveMSAA();
|
||||
dContext->flush();
|
||||
dContext->submit();
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
|
||||
|
||||
// Calling resolve again should cause no issues as it is a no-op (there is an assert in the
|
||||
// resolve op that the surface's msaa is dirty, we shouldn't hit that assert).
|
||||
surface->resolveMSAA();
|
||||
dContext->flush();
|
||||
dContext->submit();
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
|
||||
|
||||
// Try resolving in the middle of draw calls. Non automatic resolve gpus should only see the
|
||||
// results of the first draw.
|
||||
surface->getCanvas()->clear(SK_ColorGREEN);
|
||||
surface->resolveMSAA();
|
||||
surface->getCanvas()->clear(SK_ColorBLUE);
|
||||
dContext->flush();
|
||||
dContext->submit();
|
||||
if (autoResolves) {
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
|
||||
} else {
|
||||
REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorGREEN));
|
||||
}
|
||||
|
||||
// Test that a resolve between draws to a different surface doesn't cause the OpsTasks for that
|
||||
// surface to be split. Fails if we hit validation asserts in GrDrawingManager.
|
||||
// First clear out dirty msaa from previous test
|
||||
surface->flush();
|
||||
|
||||
auto otherSurface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kYes, info);
|
||||
REPORTER_ASSERT(reporter, otherSurface);
|
||||
otherSurface->getCanvas()->clear(SK_ColorRED);
|
||||
surface->resolveMSAA();
|
||||
otherSurface->getCanvas()->clear(SK_ColorBLUE);
|
||||
dContext->flush();
|
||||
dContext->submit();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user