1/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include <memory>
9
10#include "include/gpu/GrContextThreadSafeProxy.h"
11#include "src/gpu/GrContextThreadSafeProxyPriv.h"
12
13#include "include/core/SkSurfaceCharacterization.h"
14#include "src/gpu/GrBaseContextPriv.h"
15#include "src/gpu/GrCaps.h"
16#include "src/gpu/GrThreadSafeCache.h"
17#include "src/gpu/GrThreadSafePipelineBuilder.h"
18#include "src/gpu/effects/GrSkSLFP.h"
19#include "src/image/SkSurface_Gpu.h"
20
21#ifdef SK_VULKAN
22#include "src/gpu/vk/GrVkCaps.h"
23#endif
24
25static int32_t next_id() {
26    static std::atomic<int32_t> nextID{1};
27    int32_t id;
28    do {
29        id = nextID.fetch_add(1, std::memory_order_relaxed);
30    } while (id == SK_InvalidGenID);
31    return id;
32}
33
34GrContextThreadSafeProxy::GrContextThreadSafeProxy(GrBackendApi backend,
35                                                   const GrContextOptions& options)
36        : fBackend(backend), fOptions(options), fContextID(next_id()) {
37}
38
39GrContextThreadSafeProxy::~GrContextThreadSafeProxy() = default;
40
41void GrContextThreadSafeProxy::init(sk_sp<const GrCaps> caps,
42                                    sk_sp<GrThreadSafePipelineBuilder> pipelineBuilder) {
43    fCaps = std::move(caps);
44    fTextBlobCache = std::make_unique<GrTextBlobCache>(fContextID);
45    fThreadSafeCache = std::make_unique<GrThreadSafeCache>();
46    fPipelineBuilder = std::move(pipelineBuilder);
47}
48
49SkSurfaceCharacterization GrContextThreadSafeProxy::createCharacterization(
50                                     size_t cacheMaxResourceBytes,
51                                     const SkImageInfo& ii, const GrBackendFormat& backendFormat,
52                                     int sampleCnt, GrSurfaceOrigin origin,
53                                     const SkSurfaceProps& surfaceProps,
54                                     bool isMipMapped, bool willUseGLFBO0, bool isTextureable,
55                                     GrProtected isProtected, bool vkRTSupportsInputAttachment,
56                                     bool forVulkanSecondaryCommandBuffer) {
57    SkASSERT(fCaps);
58    if (!backendFormat.isValid()) {
59        return {};
60    }
61
62    SkASSERT(isTextureable || !isMipMapped);
63
64    if (GrBackendApi::kOpenGL != backendFormat.backend() && willUseGLFBO0) {
65        // The willUseGLFBO0 flags can only be used for a GL backend.
66        return {};
67    }
68
69    if (GrBackendApi::kVulkan != backendFormat.backend() &&
70        (vkRTSupportsInputAttachment || forVulkanSecondaryCommandBuffer)) {
71        // The vkRTSupportsInputAttachment and forVulkanSecondaryCommandBuffer flags can only be
72        // used for a Vulkan backend.
73        return {};
74    }
75
76    if (!fCaps->mipmapSupport()) {
77        isMipMapped = false;
78    }
79
80    if (ii.width()  < 1 || ii.width()  > fCaps->maxRenderTargetSize() ||
81        ii.height() < 1 || ii.height() > fCaps->maxRenderTargetSize()) {
82        return {};
83    }
84
85    GrColorType grColorType = SkColorTypeToGrColorType(ii.colorType());
86
87    if (!fCaps->areColorTypeAndFormatCompatible(grColorType, backendFormat)) {
88        return {};
89    }
90
91    if (!fCaps->isFormatAsColorTypeRenderable(grColorType, backendFormat, sampleCnt)) {
92        return {};
93    }
94
95    sampleCnt = fCaps->getRenderTargetSampleCount(sampleCnt, backendFormat);
96    SkASSERT(sampleCnt);
97
98    if (willUseGLFBO0 && isTextureable) {
99        return {};
100    }
101
102    if (isTextureable && !fCaps->isFormatTexturable(backendFormat, backendFormat.textureType())) {
103        // Skia doesn't agree that this is textureable.
104        return {};
105    }
106
107    if (forVulkanSecondaryCommandBuffer &&
108        (isTextureable || isMipMapped || willUseGLFBO0 || vkRTSupportsInputAttachment)) {
109        return {};
110    }
111
112    if (GrBackendApi::kVulkan == backendFormat.backend()) {
113        if (GrBackendApi::kVulkan != fBackend) {
114            return {};
115        }
116
117#ifdef SK_VULKAN
118        const GrVkCaps* vkCaps = (const GrVkCaps*) fCaps.get();
119
120        // The protection status of the characterization and the context need to match
121        if (isProtected != GrProtected(vkCaps->supportsProtectedMemory())) {
122            return {};
123        }
124#endif
125    }
126
127    return SkSurfaceCharacterization(
128            sk_ref_sp<GrContextThreadSafeProxy>(this),
129            cacheMaxResourceBytes, ii, backendFormat,
130            origin, sampleCnt,
131            SkSurfaceCharacterization::Textureable(isTextureable),
132            SkSurfaceCharacterization::MipMapped(isMipMapped),
133            SkSurfaceCharacterization::UsesGLFBO0(willUseGLFBO0),
134            SkSurfaceCharacterization::VkRTSupportsInputAttachment(vkRTSupportsInputAttachment),
135            SkSurfaceCharacterization::VulkanSecondaryCBCompatible(forVulkanSecondaryCommandBuffer),
136            isProtected,
137            surfaceProps);
138}
139
140GrBackendFormat GrContextThreadSafeProxy::defaultBackendFormat(SkColorType skColorType,
141                                                               GrRenderable renderable) const {
142    SkASSERT(fCaps);
143    GrColorType grColorType = SkColorTypeToGrColorType(skColorType);
144
145    GrBackendFormat format = fCaps->getDefaultBackendFormat(grColorType, renderable);
146    if (!format.isValid()) {
147        return GrBackendFormat();
148    }
149
150    SkASSERT(renderable == GrRenderable::kNo ||
151             fCaps->isFormatAsColorTypeRenderable(grColorType, format));
152
153    return format;
154}
155
156GrBackendFormat GrContextThreadSafeProxy::compressedBackendFormat(SkImage::CompressionType c) const {
157    SkASSERT(fCaps);
158
159    GrBackendFormat format = fCaps->getBackendFormatFromCompressionType(c);
160
161    SkASSERT(!format.isValid() || fCaps->isFormatTexturable(format, GrTextureType::k2D));
162    return format;
163}
164
165void GrContextThreadSafeProxy::abandonContext() {
166    if (!fAbandoned.exchange(true)) {
167        fTextBlobCache->freeAll();
168    }
169}
170
171bool GrContextThreadSafeProxy::abandoned() const {
172    return fAbandoned;
173}
174
175////////////////////////////////////////////////////////////////////////////////
176sk_sp<GrContextThreadSafeProxy> GrContextThreadSafeProxyPriv::Make(
177                             GrBackendApi backend,
178                             const GrContextOptions& options) {
179    return sk_sp<GrContextThreadSafeProxy>(new GrContextThreadSafeProxy(backend, options));
180}
181
182void GrContextThreadSafeProxyPriv::init(sk_sp<const GrCaps> caps,
183                                        sk_sp<GrThreadSafePipelineBuilder> builder) const {
184    fProxy->init(std::move(caps), std::move(builder));
185}
186
187