xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/mtl_occlusion_query_pool.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1//
2// Copyright (c) 2020 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// mtl_occlusion_query_pool: A visibility pool for allocating visibility query within
7// one render pass.
8//
9
10#include "libANGLE/renderer/metal/mtl_occlusion_query_pool.h"
11
12#include "libANGLE/renderer/metal/ContextMtl.h"
13#include "libANGLE/renderer/metal/DisplayMtl.h"
14#include "libANGLE/renderer/metal/QueryMtl.h"
15
16namespace rx
17{
18namespace mtl
19{
20
21// OcclusionQueryPool implementation
22OcclusionQueryPool::OcclusionQueryPool() {}
23OcclusionQueryPool::~OcclusionQueryPool() {}
24
25void OcclusionQueryPool::destroy(ContextMtl *contextMtl)
26{
27    mRenderPassResultsPool = nullptr;
28    for (QueryMtl *allocatedQuery : mAllocatedQueries)
29    {
30        if (!allocatedQuery)
31        {
32            continue;
33        }
34        allocatedQuery->clearAllocatedVisibilityOffsets();
35    }
36    mAllocatedQueries.clear();
37}
38
39angle::Result OcclusionQueryPool::allocateQueryOffset(ContextMtl *contextMtl,
40                                                      QueryMtl *query,
41                                                      bool clearOldValue)
42{
43    // Only query that already has allocated offset or first query of the render pass is allowed to
44    // keep old value. Other queries must be reset to zero before counting the samples visibility in
45    // draw calls.
46    ASSERT(clearOldValue || mAllocatedQueries.empty() ||
47           !query->getAllocatedVisibilityOffsets().empty());
48
49    uint32_t currentOffset =
50        static_cast<uint32_t>(mAllocatedQueries.size()) * kOcclusionQueryResultSize;
51    if (!mRenderPassResultsPool)
52    {
53        ASSERT(!mUsed);
54        // First allocation
55        ANGLE_TRY(Buffer::MakeBufferWithStorageMode(contextMtl, MTLStorageModePrivate,
56                                                    kOcclusionQueryResultSize, nullptr,
57                                                    &mRenderPassResultsPool));
58        mRenderPassResultsPool->get().label = @"OcclusionQueryPool";
59    }
60    else if (currentOffset + kOcclusionQueryResultSize > mRenderPassResultsPool->size())
61    {
62        // Double the capacity
63        ANGLE_TRY(Buffer::MakeBufferWithStorageMode(contextMtl, MTLStorageModePrivate,
64                                                    mRenderPassResultsPool->size() * 2, nullptr,
65                                                    &mRenderPassResultsPool));
66        mUsed                               = false;
67        mRenderPassResultsPool->get().label = @"OcclusionQueryPool";
68    }
69
70    if (clearOldValue)
71    {
72        // If old value is not needed, deallocate any offset previously allocated for this query.
73        deallocateQueryOffset(contextMtl, query);
74    }
75    if (query->getAllocatedVisibilityOffsets().empty())
76    {
77        mAllocatedQueries.push_back(query);
78        query->setFirstAllocatedVisibilityOffset(currentOffset);
79    }
80    else
81    {
82        // Additional offset allocated for a query is only allowed if it is a continuous region.
83        ASSERT(currentOffset ==
84               query->getAllocatedVisibilityOffsets().back() + kOcclusionQueryResultSize);
85        // Just reserve an empty slot in the allocated query array
86        mAllocatedQueries.push_back(nullptr);
87        query->addAllocatedVisibilityOffset();
88    }
89
90    if (currentOffset == 0)
91    {
92        mResetFirstQuery = clearOldValue;
93        if (!clearOldValue && !contextMtl->getDisplay()->getFeatures().allowBufferReadWrite.enabled)
94        {
95            // If old value of first query needs to be retained and device doesn't support buffer
96            // read-write, we need an additional offset to store the old value of the query.
97            return allocateQueryOffset(contextMtl, query, false);
98        }
99    }
100
101    return angle::Result::Continue;
102}
103
104void OcclusionQueryPool::deallocateQueryOffset(ContextMtl *contextMtl, QueryMtl *query)
105{
106    if (query->getAllocatedVisibilityOffsets().empty())
107    {
108        return;
109    }
110
111    mAllocatedQueries[query->getAllocatedVisibilityOffsets().front() / kOcclusionQueryResultSize] =
112        nullptr;
113    query->clearAllocatedVisibilityOffsets();
114}
115
116void OcclusionQueryPool::resolveVisibilityResults(ContextMtl *contextMtl)
117{
118    if (mAllocatedQueries.empty())
119    {
120        return;
121    }
122
123    RenderUtils &utils              = contextMtl->getDisplay()->getUtils();
124    BlitCommandEncoder *blitEncoder = nullptr;
125    // Combine the values stored in the offsets allocated for first query
126    if (mAllocatedQueries[0])
127    {
128        const BufferRef &dstBuf = mAllocatedQueries[0]->getVisibilityResultBuffer();
129        const VisibilityBufferOffsetsMtl &allocatedOffsets =
130            mAllocatedQueries[0]->getAllocatedVisibilityOffsets();
131        if (!mResetFirstQuery &&
132            !contextMtl->getDisplay()->getFeatures().allowBufferReadWrite.enabled)
133        {
134            // If we cannot read and write to the same buffer in shader. We need to copy the old
135            // value of first query to first offset allocated for it.
136            blitEncoder = contextMtl->getBlitCommandEncoder();
137            blitEncoder->copyBuffer(dstBuf, 0, mRenderPassResultsPool, allocatedOffsets.front(),
138                                    kOcclusionQueryResultSize);
139            utils.combineVisibilityResult(contextMtl, false, allocatedOffsets,
140                                          mRenderPassResultsPool, dstBuf);
141        }
142        else
143        {
144            utils.combineVisibilityResult(contextMtl, !mResetFirstQuery, allocatedOffsets,
145                                          mRenderPassResultsPool, dstBuf);
146        }
147    }
148
149    // Combine the values stored in the offsets allocated for each of the remaining queries
150    for (size_t i = 1; i < mAllocatedQueries.size(); ++i)
151    {
152        QueryMtl *query = mAllocatedQueries[i];
153        if (!query)
154        {
155            continue;
156        }
157
158        const BufferRef &dstBuf = mAllocatedQueries[i]->getVisibilityResultBuffer();
159        const VisibilityBufferOffsetsMtl &allocatedOffsets =
160            mAllocatedQueries[i]->getAllocatedVisibilityOffsets();
161        utils.combineVisibilityResult(contextMtl, false, allocatedOffsets, mRenderPassResultsPool,
162                                      dstBuf);
163    }
164
165    // Request synchronization and cleanup
166    blitEncoder = contextMtl->getBlitCommandEncoder();
167    for (size_t i = 0; i < mAllocatedQueries.size(); ++i)
168    {
169        QueryMtl *query = mAllocatedQueries[i];
170        if (!query)
171        {
172            continue;
173        }
174
175        const BufferRef &dstBuf = mAllocatedQueries[i]->getVisibilityResultBuffer();
176
177        dstBuf->syncContent(contextMtl, blitEncoder);
178
179        query->clearAllocatedVisibilityOffsets();
180    }
181
182    mAllocatedQueries.clear();
183}
184
185void OcclusionQueryPool::prepareRenderPassVisibilityPoolBuffer(ContextMtl *contextMtl)
186{
187    if (mAllocatedQueries.empty())
188    {
189        return;
190    }
191
192    // If the current visibility pool buffer was not used before,
193    // ensure that it will be cleared next time.
194    if (!mUsed)
195    {
196        mUsed = true;
197        return;
198    }
199
200    mUsed = false;
201
202    // If the current visibility pool buffer was used before,
203    // ensure that it does not contain previous results.
204    auto blitEncoder = contextMtl->getBlitCommandEncoderWithoutEndingRenderEncoder();
205    blitEncoder->fillBuffer(mRenderPassResultsPool,
206                            NSMakeRange(0, mAllocatedQueries.size() * kOcclusionQueryResultSize),
207                            0);
208    blitEncoder->endEncoding();
209}
210
211}  // namespace mtl
212}  // namespace rx
213