xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/QueryMtl.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// QueryMtl.mm:
7//    Defines the class interface for QueryMtl, implementing QueryImpl.
8//
9
10#include "libANGLE/renderer/metal/QueryMtl.h"
11
12#include "libANGLE/renderer/metal/ContextMtl.h"
13
14namespace rx
15{
16QueryMtl::QueryMtl(gl::QueryType type) : QueryImpl(type) {}
17
18QueryMtl::~QueryMtl() {}
19
20void QueryMtl::onDestroy(const gl::Context *context)
21{
22    ContextMtl *contextMtl = mtl::GetImpl(context);
23    if (!getAllocatedVisibilityOffsets().empty())
24    {
25        contextMtl->onOcclusionQueryDestroy(context, this);
26    }
27    mVisibilityResultBuffer = nullptr;
28}
29
30angle::Result QueryMtl::begin(const gl::Context *context)
31{
32    ContextMtl *contextMtl = mtl::GetImpl(context);
33    switch (getType())
34    {
35        case gl::QueryType::AnySamples:
36        case gl::QueryType::AnySamplesConservative:
37            if (!mVisibilityResultBuffer)
38            {
39                // Allocate buffer
40                ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, mtl::kOcclusionQueryResultSize,
41                                                  nullptr, &mVisibilityResultBuffer));
42
43                ANGLE_MTL_OBJC_SCOPE
44                {
45                    mVisibilityResultBuffer->get().label =
46                        [NSString stringWithFormat:@"QueryMtl=%p", this];
47                }
48            }
49
50            ANGLE_TRY(contextMtl->onOcclusionQueryBegin(context, this));
51            break;
52        case gl::QueryType::TransformFeedbackPrimitivesWritten:
53            mTransformFeedbackPrimitivesDrawn = 0;
54            break;
55        case gl::QueryType::TimeElapsed:
56        {
57            // End any command buffer being encoded, to get a clean boundary for beginning
58            // measurement.
59            contextMtl->flushCommandBuffer(mtl::NoWait);
60            mtl::CommandQueue &queue = contextMtl->getDisplay()->cmdQueue();
61            if (mTimeElapsedEntry != 0)
62            {
63                queue.deleteTimeElapsedEntry(mTimeElapsedEntry);
64                mTimeElapsedEntry = 0;
65            }
66            mTimeElapsedEntry = queue.allocateTimeElapsedEntry();
67            queue.setActiveTimeElapsedEntry(mTimeElapsedEntry);
68            break;
69        }
70        default:
71            UNIMPLEMENTED();
72            break;
73    }
74
75    return angle::Result::Continue;
76}
77angle::Result QueryMtl::end(const gl::Context *context)
78{
79    ContextMtl *contextMtl = mtl::GetImpl(context);
80    switch (getType())
81    {
82        case gl::QueryType::AnySamples:
83        case gl::QueryType::AnySamplesConservative:
84            contextMtl->onOcclusionQueryEnd(context, this);
85            break;
86        case gl::QueryType::TransformFeedbackPrimitivesWritten:
87            onTransformFeedbackEnd(context);
88            break;
89        case gl::QueryType::TimeElapsed:
90        {
91            // End any command buffer being encoded, to get a clean boundary for ending measurement.
92            contextMtl->flushCommandBuffer(mtl::NoWait);
93            mtl::CommandQueue &queue = contextMtl->getDisplay()->cmdQueue();
94            queue.setActiveTimeElapsedEntry(0);
95            break;
96        }
97        default:
98            UNIMPLEMENTED();
99            break;
100    }
101    return angle::Result::Continue;
102}
103angle::Result QueryMtl::queryCounter(const gl::Context *context)
104{
105    UNIMPLEMENTED();
106    return angle::Result::Continue;
107}
108
109template <typename T>
110angle::Result QueryMtl::waitAndGetResult(const gl::Context *context, T *params)
111{
112    ASSERT(params);
113    ContextMtl *contextMtl = mtl::GetImpl(context);
114    switch (getType())
115    {
116        case gl::QueryType::AnySamples:
117        case gl::QueryType::AnySamplesConservative:
118        {
119            ASSERT(mVisibilityResultBuffer);
120            if (mVisibilityResultBuffer->hasPendingWorks(contextMtl))
121            {
122                contextMtl->flushCommandBuffer(mtl::NoWait);
123            }
124            // map() will wait for the pending GPU works to finish
125            const uint8_t *visibilityResultBytes = mVisibilityResultBuffer->mapReadOnly(contextMtl);
126            uint64_t queryResult;
127            memcpy(&queryResult, visibilityResultBytes, sizeof(queryResult));
128            mVisibilityResultBuffer->unmap(contextMtl);
129
130            *params = queryResult ? GL_TRUE : GL_FALSE;
131        }
132        break;
133        case gl::QueryType::TransformFeedbackPrimitivesWritten:
134            *params = static_cast<T>(mTransformFeedbackPrimitivesDrawn);
135            break;
136        case gl::QueryType::TimeElapsed:
137        {
138            ASSERT(mTimeElapsedEntry != 0);
139            mtl::CommandQueue &queue = contextMtl->getDisplay()->cmdQueue();
140            if (!queue.isTimeElapsedEntryComplete(mTimeElapsedEntry))
141            {
142                contextMtl->flushCommandBuffer(mtl::WaitUntilFinished);
143            }
144            ASSERT(queue.isTimeElapsedEntryComplete(mTimeElapsedEntry));
145            double nanos    = queue.getTimeElapsedEntryInSeconds(mTimeElapsedEntry) * 1e9;
146            uint64_t result = static_cast<uint64_t>(nanos);
147            *params         = static_cast<T>(result);
148            break;
149        }
150        default:
151            UNIMPLEMENTED();
152            break;
153    }
154    return angle::Result::Continue;
155}
156
157angle::Result QueryMtl::isResultAvailable(const gl::Context *context, bool *available)
158{
159    ASSERT(available);
160    ContextMtl *contextMtl = mtl::GetImpl(context);
161    // glGetQueryObjectuiv implicitly flush any pending works related to the query
162    switch (getType())
163    {
164        case gl::QueryType::AnySamples:
165        case gl::QueryType::AnySamplesConservative:
166            ASSERT(mVisibilityResultBuffer);
167            if (mVisibilityResultBuffer->hasPendingWorks(contextMtl))
168            {
169                contextMtl->flushCommandBuffer(mtl::NoWait);
170            }
171
172            *available = !mVisibilityResultBuffer->isBeingUsedByGPU(contextMtl);
173            break;
174        case gl::QueryType::TransformFeedbackPrimitivesWritten:
175            *available = true;
176            break;
177        case gl::QueryType::TimeElapsed:
178            *available =
179                contextMtl->getDisplay()->cmdQueue().isTimeElapsedEntryComplete(mTimeElapsedEntry);
180            break;
181        default:
182            UNIMPLEMENTED();
183            break;
184    }
185    return angle::Result::Continue;
186}
187
188angle::Result QueryMtl::getResult(const gl::Context *context, GLint *params)
189{
190    return waitAndGetResult(context, params);
191}
192angle::Result QueryMtl::getResult(const gl::Context *context, GLuint *params)
193{
194    return waitAndGetResult(context, params);
195}
196angle::Result QueryMtl::getResult(const gl::Context *context, GLint64 *params)
197{
198    return waitAndGetResult(context, params);
199}
200angle::Result QueryMtl::getResult(const gl::Context *context, GLuint64 *params)
201{
202    return waitAndGetResult(context, params);
203}
204
205void QueryMtl::resetVisibilityResult(ContextMtl *contextMtl)
206{
207    // Occlusion query buffer must be allocated in QueryMtl::begin
208    ASSERT(mVisibilityResultBuffer);
209
210    // Fill the query's buffer with zeros
211    auto blitEncoder = contextMtl->getBlitCommandEncoder();
212    blitEncoder->fillBuffer(mVisibilityResultBuffer, NSMakeRange(0, mtl::kOcclusionQueryResultSize),
213                            0);
214    mVisibilityResultBuffer->syncContent(contextMtl, blitEncoder);
215}
216
217void QueryMtl::onTransformFeedbackEnd(const gl::Context *context)
218{
219    gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
220    if (transformFeedback)
221    {
222        mTransformFeedbackPrimitivesDrawn += transformFeedback->getPrimitivesDrawn();
223    }
224}
225
226void QueryMtl::onContextMakeCurrent(const gl::Context *context)
227{
228    // At present this should only be called for time elapsed queries.
229    ASSERT(getType() == gl::QueryType::TimeElapsed);
230    ContextMtl *contextMtl = mtl::GetImpl(context);
231    contextMtl->getDisplay()->cmdQueue().setActiveTimeElapsedEntry(mTimeElapsedEntry);
232}
233
234void QueryMtl::onContextUnMakeCurrent(const gl::Context *context)
235{
236    // At present this should only be called for time elapsed queries.
237    ASSERT(getType() == gl::QueryType::TimeElapsed);
238    ContextMtl *contextMtl = mtl::GetImpl(context);
239    contextMtl->getDisplay()->cmdQueue().setActiveTimeElapsedEntry(0);
240}
241
242}  // namespace rx
243