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