| /* | |
| * Copyright 2017 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/mock/GrMockGpu.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/gpu/ganesh/mock/GrMockBackendSurface.h" | |
| #include "include/private/SkDebug.h" | |
| #include "include/private/SkMath.h" | |
| #include "src/gpu/ganesh/GrCaps.h" | |
| #include "src/gpu/ganesh/GrGpuBuffer.h" | |
| #include "src/gpu/ganesh/GrRenderTarget.h" | |
| #include "src/gpu/ganesh/GrTexture.h" | |
| #include "src/gpu/ganesh/GrThreadSafePipelineBuilder.h" // IWYU pragma: keep | |
| #include "src/gpu/ganesh/mock/GrMockAttachment.h" | |
| #include "src/gpu/ganesh/mock/GrMockBuffer.h" | |
| #include "src/gpu/ganesh/mock/GrMockCaps.h" | |
| #include "src/gpu/ganesh/mock/GrMockOpsRenderPass.h" | |
| #include "src/gpu/ganesh/mock/GrMockTexture.h" | |
| #include <atomic> | |
| using namespace skia_private; | |
| int GrMockGpu::NextInternalTextureID() { | |
| static std::atomic<int> nextID{1}; | |
| int id; | |
| do { | |
| id = nextID.fetch_add(1, std::memory_order_relaxed); | |
| } while (0 == id); // Reserve 0 for an invalid ID. | |
| return id; | |
| } | |
| int GrMockGpu::NextExternalTextureID() { | |
| // We use negative ints for the "testing only external textures" so they can easily be | |
| // identified when debugging. | |
| static std::atomic<int> nextID{-1}; | |
| return nextID.fetch_add(-1, std::memory_order_relaxed); | |
| } | |
| int GrMockGpu::NextInternalRenderTargetID() { | |
| // We start off with large numbers to differentiate from texture IDs, even though they're | |
| // technically in a different space. | |
| static std::atomic<int> nextID{SK_MaxS32}; | |
| return nextID.fetch_add(-1, std::memory_order_relaxed); | |
| } | |
| int GrMockGpu::NextExternalRenderTargetID() { | |
| // We use large negative ints for the "testing only external render targets" so they can easily | |
| // be identified when debugging. | |
| static std::atomic<int> nextID{SK_MinS32}; | |
| return nextID.fetch_add(1, std::memory_order_relaxed); | |
| } | |
| std::unique_ptr<GrGpu> GrMockGpu::Make(const GrMockOptions* mockOptions, | |
| const GrContextOptions& contextOptions, | |
| GrDirectContext* direct) { | |
| static const GrMockOptions kDefaultOptions = GrMockOptions(); | |
| if (!mockOptions) { | |
| mockOptions = &kDefaultOptions; | |
| } | |
| return std::unique_ptr<GrGpu>(new GrMockGpu(direct, *mockOptions, contextOptions)); | |
| } | |
| GrOpsRenderPass* GrMockGpu::onGetOpsRenderPass(GrRenderTarget* rt, | |
| bool /*useMSAASurface*/, | |
| GrAttachment*, | |
| GrSurfaceOrigin origin, | |
| const SkIRect& bounds, | |
| const GrOpsRenderPass::LoadAndStoreInfo& colorInfo, | |
| const GrOpsRenderPass::StencilLoadAndStoreInfo&, | |
| const TArray<GrSurfaceProxy*,true>& sampledProxies, | |
| GrXferBarrierFlags renderPassXferBarriers) { | |
| return new GrMockOpsRenderPass(this, rt, origin, colorInfo); | |
| } | |
| void GrMockGpu::submit(GrOpsRenderPass* renderPass) { | |
| for (int i = 0; i < static_cast<GrMockOpsRenderPass*>(renderPass)->numDraws(); ++i) { | |
| fStats.incNumDraws(); | |
| } | |
| delete renderPass; | |
| } | |
| GrMockGpu::GrMockGpu(GrDirectContext* direct, const GrMockOptions& options, | |
| const GrContextOptions& contextOptions) | |
| : INHERITED(direct) | |
| , fMockOptions(options) { | |
| this->initCaps(sk_make_sp<GrMockCaps>(contextOptions, options)); | |
| } | |
| GrMockGpu::~GrMockGpu() {} | |
| GrThreadSafePipelineBuilder* GrMockGpu::pipelineBuilder() { | |
| return nullptr; | |
| } | |
| sk_sp<GrThreadSafePipelineBuilder> GrMockGpu::refPipelineBuilder() { | |
| return nullptr; | |
| } | |
| sk_sp<GrTexture> GrMockGpu::onCreateTexture(SkISize dimensions, | |
| const GrBackendFormat& format, | |
| GrRenderable renderable, | |
| int renderTargetSampleCnt, | |
| skgpu::Budgeted budgeted, | |
| GrProtected isProtected, | |
| int mipLevelCount, | |
| uint32_t levelClearMask, | |
| std::string_view label) { | |
| if (fMockOptions.fFailTextureAllocations) { | |
| return nullptr; | |
| } | |
| // Compressed formats should go through onCreateCompressedTexture | |
| SkASSERT(GrBackendFormats::AsMockCompressionType(format) == SkTextureCompressionType::kNone); | |
| GrColorType ct = GrBackendFormats::AsMockColorType(format); | |
| SkASSERT(ct != GrColorType::kUnknown); | |
| GrMipmapStatus mipmapStatus = | |
| mipLevelCount > 1 ? GrMipmapStatus::kDirty : GrMipmapStatus::kNotAllocated; | |
| GrMockTextureInfo texInfo(ct, SkTextureCompressionType::kNone, NextInternalTextureID(), | |
| isProtected); | |
| if (renderable == GrRenderable::kYes) { | |
| GrMockRenderTargetInfo rtInfo(ct, NextInternalRenderTargetID(), isProtected); | |
| return sk_sp<GrTexture>(new GrMockTextureRenderTarget(this, budgeted, dimensions, | |
| renderTargetSampleCnt, | |
| mipmapStatus, | |
| texInfo, | |
| rtInfo, | |
| label)); | |
| } | |
| return sk_sp<GrTexture>(new GrMockTexture( | |
| this, budgeted, dimensions, mipmapStatus, texInfo, label)); | |
| } | |
| // TODO: why no 'isProtected' ?! | |
| sk_sp<GrTexture> GrMockGpu::onCreateCompressedTexture(SkISize dimensions, | |
| const GrBackendFormat& format, | |
| skgpu::Budgeted budgeted, | |
| skgpu::Mipmapped mipmapped, | |
| GrProtected isProtected, | |
| const void* data, | |
| size_t dataSize) { | |
| if (fMockOptions.fFailTextureAllocations) { | |
| return nullptr; | |
| } | |
| #ifdef SK_DEBUG | |
| // Uncompressed formats should go through onCreateTexture | |
| SkTextureCompressionType compression = GrBackendFormats::AsMockCompressionType(format); | |
| SkASSERT(compression != SkTextureCompressionType::kNone); | |
| #endif | |
| GrMipmapStatus mipmapStatus = (mipmapped == skgpu::Mipmapped::kYes) | |
| ? GrMipmapStatus::kValid | |
| : GrMipmapStatus::kNotAllocated; | |
| GrMockTextureInfo texInfo(GrColorType::kUnknown, | |
| GrBackendFormats::AsMockCompressionType(format), | |
| NextInternalTextureID(), | |
| isProtected); | |
| return sk_sp<GrTexture>(new GrMockTexture( | |
| this, budgeted, dimensions, mipmapStatus, texInfo, | |
| /*label=*/"MockGpu_CreateCompressedTexture")); | |
| } | |
| sk_sp<GrTexture> GrMockGpu::onWrapBackendTexture(const GrBackendTexture& tex, | |
| GrWrapOwnership ownership, | |
| GrWrapCacheable wrapType, | |
| GrIOType ioType) { | |
| SkASSERT(tex.backend() == GrBackendApi::kMock); | |
| GrMockTextureInfo texInfo = GrBackendTextures::GetMockTextureInfo(tex); | |
| SkTextureCompressionType compression = texInfo.compressionType(); | |
| if (compression != SkTextureCompressionType::kNone) { | |
| return nullptr; | |
| } | |
| GrMipmapStatus mipmapStatus = tex.hasMipmaps() ? GrMipmapStatus::kValid | |
| : GrMipmapStatus::kNotAllocated; | |
| return sk_sp<GrTexture>(new GrMockTexture(this, | |
| tex.dimensions(), | |
| mipmapStatus, | |
| texInfo, | |
| wrapType, | |
| ioType, | |
| /*label=*/"MockGpu_WrapBackendTexture")); | |
| } | |
| sk_sp<GrTexture> GrMockGpu::onWrapCompressedBackendTexture(const GrBackendTexture& tex, | |
| GrWrapOwnership ownership, | |
| GrWrapCacheable wrapType) { | |
| return nullptr; | |
| } | |
| sk_sp<GrTexture> GrMockGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex, | |
| int sampleCnt, | |
| GrWrapOwnership ownership, | |
| GrWrapCacheable cacheable) { | |
| SkASSERT(tex.backend() == GrBackendApi::kMock); | |
| GrMockTextureInfo texInfo = GrBackendTextures::GetMockTextureInfo(tex); | |
| SkASSERT(texInfo.compressionType() == SkTextureCompressionType::kNone); | |
| GrMipmapStatus mipmapStatus = | |
| tex.hasMipmaps() ? GrMipmapStatus::kValid : GrMipmapStatus::kNotAllocated; | |
| // The client gave us the texture ID but we supply the render target ID. | |
| GrMockRenderTargetInfo rtInfo(texInfo.colorType(), NextInternalRenderTargetID(), | |
| texInfo.getProtected()); | |
| return sk_sp<GrTexture>( | |
| new GrMockTextureRenderTarget(this, | |
| tex.dimensions(), | |
| sampleCnt, | |
| mipmapStatus, | |
| texInfo, | |
| rtInfo, | |
| cacheable, | |
| /*label=*/"MockGpu_WrapRenderableBackendTexture")); | |
| } | |
| sk_sp<GrRenderTarget> GrMockGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& rt) { | |
| SkASSERT(rt.backend() == GrBackendApi::kMock); | |
| GrMockRenderTargetInfo info = GrBackendRenderTargets::GetMockRenderTargetInfo(rt); | |
| return sk_sp<GrRenderTarget>( | |
| new GrMockRenderTarget(this, | |
| GrMockRenderTarget::kWrapped, | |
| rt.dimensions(), | |
| rt.sampleCnt(), | |
| info, | |
| /*label=*/"MockGpu_WrapBackendRenderTarget")); | |
| } | |
| sk_sp<GrGpuBuffer> GrMockGpu::onCreateBuffer(size_t sizeInBytes, | |
| GrGpuBufferType type, | |
| GrAccessPattern accessPattern) { | |
| return sk_sp<GrGpuBuffer>( | |
| new GrMockBuffer(this, sizeInBytes, type, accessPattern, | |
| /*label=*/"MockGpu_CreateBuffer")); | |
| } | |
| sk_sp<GrAttachment> GrMockGpu::makeStencilAttachment(const GrBackendFormat& /*colorFormat*/, | |
| SkISize dimensions, int numStencilSamples) { | |
| fStats.incStencilAttachmentCreates(); | |
| return sk_sp<GrAttachment>(new GrMockAttachment(this, | |
| dimensions, | |
| GrAttachment::UsageFlags::kStencilAttachment, | |
| numStencilSamples, | |
| /*label=*/"MockGpu_MakeStencilAttachment")); | |
| } | |
| GrBackendTexture GrMockGpu::onCreateBackendTexture(SkISize dimensions, | |
| const GrBackendFormat& format, | |
| GrRenderable, | |
| skgpu::Mipmapped mipmapped, | |
| GrProtected isProtected, | |
| std::string_view label) { | |
| SkTextureCompressionType compression = GrBackendFormats::AsMockCompressionType(format); | |
| if (compression != SkTextureCompressionType::kNone) { | |
| return {}; // should go through onCreateCompressedBackendTexture | |
| } | |
| auto colorType = GrBackendFormats::AsMockColorType(format); | |
| if (!this->caps()->isFormatTexturable(format, GrTextureType::k2D)) { | |
| return GrBackendTexture(); // invalid | |
| } | |
| GrMockTextureInfo info(colorType, SkTextureCompressionType::kNone, NextExternalTextureID(), | |
| isProtected); | |
| fOutstandingTestingOnlyTextureIDs.add(info.id()); | |
| return GrBackendTextures::MakeMock(dimensions.width(), dimensions.height(), mipmapped, info); | |
| } | |
| GrBackendTexture GrMockGpu::onCreateCompressedBackendTexture(SkISize dimensions, | |
| const GrBackendFormat& format, | |
| skgpu::Mipmapped mipmapped, | |
| GrProtected isProtected) { | |
| SkTextureCompressionType compression = GrBackendFormats::AsMockCompressionType(format); | |
| if (compression == SkTextureCompressionType::kNone) { | |
| return {}; // should go through onCreateBackendTexture | |
| } | |
| if (!this->caps()->isFormatTexturable(format, GrTextureType::k2D)) { | |
| return {}; | |
| } | |
| GrMockTextureInfo info(GrColorType::kUnknown, compression, NextExternalTextureID(), | |
| isProtected); | |
| fOutstandingTestingOnlyTextureIDs.add(info.id()); | |
| return GrBackendTextures::MakeMock(dimensions.width(), dimensions.height(), mipmapped, info); | |
| } | |
| void GrMockGpu::onDeleteBackendTexture(const GrBackendTexture& tex) { | |
| SkASSERT(tex.backend() == GrBackendApi::kMock); | |
| GrMockTextureInfo info = GrBackendTextures::GetMockTextureInfo(tex); | |
| fOutstandingTestingOnlyTextureIDs.remove(info.id()); | |
| } | |
| #if defined(GPU_TEST_UTILS) | |
| bool GrMockGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const { | |
| SkASSERT(tex.backend() == GrBackendApi::kMock); | |
| GrMockTextureInfo info = GrBackendTextures::GetMockTextureInfo(tex); | |
| return fOutstandingTestingOnlyTextureIDs.contains(info.id()); | |
| } | |
| GrBackendRenderTarget GrMockGpu::createTestingOnlyBackendRenderTarget(SkISize dimensions, | |
| GrColorType colorType, | |
| int sampleCnt, | |
| GrProtected isProtected) { | |
| GrMockRenderTargetInfo info(colorType, NextExternalRenderTargetID(), isProtected); | |
| static constexpr int kStencilBits = 8; | |
| return GrBackendRenderTargets::MakeMock( | |
| dimensions.width(), dimensions.height(), sampleCnt, kStencilBits, info); | |
| } | |
| void GrMockGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget&) {} | |
| #endif |