Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 50704dc

Browse files
kakashidinhoCommit Bot
authored andcommitted
Metal: Implement EXT_occlusion_query_boolean.
- Metal's occlusion(called visibility) query operates per render pass. Implementation details are in src/libANGLE/renderer/metal/doc/OcclusionQueries.md. - New tests: - OcclusionQueriesTest.ClearNotCounted. - OcclusionQueriesTest.MultiQueries (failure on OpenGL/D3D11 back-end). Bug: angleproject:2634 Change-Id: Idd1327b5472d0e8c2b69307a7f04a1da4a847a40 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2336121 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: back sept 10 - Jamie Madill <jmadill@chromium.org> Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
1 parent f005191 commit 50704dc

19 files changed

+1328
-88
lines changed

src/libANGLE/renderer/metal/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ _metal_backend_sources = [
2323
"FrameBufferMtl.mm",
2424
"ProgramMtl.h",
2525
"ProgramMtl.mm",
26+
"QueryMtl.h",
27+
"QueryMtl.mm",
2628
"RenderBufferMtl.h",
2729
"RenderBufferMtl.mm",
2830
"RenderTargetMtl.h",
@@ -46,6 +48,8 @@ _metal_backend_sources = [
4648
"mtl_format_utils.mm",
4749
"mtl_glslang_utils.h",
4850
"mtl_glslang_utils.mm",
51+
"mtl_occlusion_query_pool.h",
52+
"mtl_occlusion_query_pool.mm",
4953
"mtl_render_utils.h",
5054
"mtl_render_utils.mm",
5155
"mtl_resources.h",

src/libANGLE/renderer/metal/ContextMtl.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "libANGLE/renderer/ContextImpl.h"
1818
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
1919
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
20+
#include "libANGLE/renderer/metal/mtl_occlusion_query_pool.h"
2021
#include "libANGLE/renderer/metal/mtl_resources.h"
2122
#include "libANGLE/renderer/metal/mtl_state_cache.h"
2223
#include "libANGLE/renderer/metal/mtl_utils.h"
@@ -272,6 +273,21 @@ class ContextMtl : public ContextImpl, public mtl::Context
272273
bool renderPassChanged);
273274
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
274275

276+
// Invoke by QueryMtl
277+
angle::Result onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query);
278+
void onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query);
279+
void onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query);
280+
281+
// Useful for temporarily pause then restart occlusion query during clear/blit with draw.
282+
bool hasActiveOcclusionQuery() const { return mOcclusionQuery; }
283+
// Disable the occlusion query in the current render pass.
284+
// The render pass must already started.
285+
void disableActiveOcclusionQueryInRenderPass();
286+
// Re-enable the occlusion query in the current render pass.
287+
// The render pass must already started.
288+
// NOTE: the old query's result will be retained and combined with the new result.
289+
angle::Result restartActiveOcclusionQueryInRenderPass();
290+
275291
const MTLClearColor &getClearColorValue() const;
276292
MTLColorWriteMask getColorMask() const;
277293
float getClearDepthValue() const;
@@ -292,7 +308,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
292308

293309
// Recommended to call these methods to end encoding instead of invoking the encoder's
294310
// endEncoding() directly.
295-
void endEncoding(mtl::RenderCommandEncoder *encoder);
311+
void endRenderEncoding(mtl::RenderCommandEncoder *encoder);
296312
// Ends any active command encoder
297313
void endEncoding(bool forceSaveRenderPassContent);
298314

@@ -406,6 +422,8 @@ class ContextMtl : public ContextImpl, public mtl::Context
406422
gl::PrimitiveMode primitiveMode,
407423
Optional<mtl::RenderPipelineDesc> *changedPipelineDesc);
408424

425+
angle::Result startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue);
426+
409427
// Dirty bits.
410428
enum DirtyBitType : size_t
411429
{
@@ -470,6 +488,8 @@ class ContextMtl : public ContextImpl, public mtl::Context
470488
float values[4];
471489
};
472490

491+
mtl::OcclusionQueryPool mOcclusionQueryPool;
492+
473493
mtl::CommandBuffer mCmdBuffer;
474494
mtl::RenderCommandEncoder mRenderEncoder;
475495
mtl::BlitCommandEncoder mBlitEncoder;
@@ -479,6 +499,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
479499
FramebufferMtl *mDrawFramebuffer = nullptr;
480500
VertexArrayMtl *mVertexArray = nullptr;
481501
ProgramMtl *mProgram = nullptr;
502+
QueryMtl *mOcclusionQuery = nullptr;
482503

483504
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
484505

src/libANGLE/renderer/metal/ContextMtl.mm

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "libANGLE/renderer/metal/DisplayMtl.h"
1818
#include "libANGLE/renderer/metal/FrameBufferMtl.h"
1919
#include "libANGLE/renderer/metal/ProgramMtl.h"
20+
#include "libANGLE/renderer/metal/QueryMtl.h"
2021
#include "libANGLE/renderer/metal/RenderBufferMtl.h"
2122
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
2223
#include "libANGLE/renderer/metal/ShaderMtl.h"
@@ -97,7 +98,7 @@ bool NeedToInvertDepthRange(float near, float far)
9798
: ContextImpl(state, errorSet),
9899
mtl::Context(display),
99100
mCmdBuffer(&display->cmdQueue()),
100-
mRenderEncoder(&mCmdBuffer),
101+
mRenderEncoder(&mCmdBuffer, mOcclusionQueryPool),
101102
mBlitEncoder(&mCmdBuffer),
102103
mComputeEncoder(&mCmdBuffer)
103104
{}
@@ -122,6 +123,7 @@ bool NeedToInvertDepthRange(float near, float far)
122123
{
123124
mTriFanIndexBuffer.destroy(this);
124125
mLineLoopIndexBuffer.destroy(this);
126+
mOcclusionQueryPool.destroy(this);
125127

126128
mIncompleteTextures.onDestroy(context);
127129
mIncompleteTexturesInitialized = false;
@@ -927,9 +929,7 @@ bool NeedToInvertDepthRange(float near, float far)
927929
// Query and Fence creation
928930
QueryImpl *ContextMtl::createQuery(gl::QueryType type)
929931
{
930-
// NOTE(hqle): ES 3.0
931-
UNIMPLEMENTED();
932-
return nullptr;
932+
return new QueryMtl(type);
933933
}
934934
FenceNVImpl *ContextMtl::createFenceNV()
935935
{
@@ -1131,9 +1131,18 @@ bool NeedToInvertDepthRange(float near, float far)
11311131
return mIncompleteTextures.getIncompleteTexture(context, type, nullptr, textureOut);
11321132
}
11331133

1134-
void ContextMtl::endEncoding(mtl::RenderCommandEncoder *encoder)
1134+
void ContextMtl::endRenderEncoding(mtl::RenderCommandEncoder *encoder)
11351135
{
1136+
// End any pending visibility query in the render pass
1137+
if (mOcclusionQuery)
1138+
{
1139+
disableActiveOcclusionQueryInRenderPass();
1140+
}
1141+
11361142
encoder->endEncoding();
1143+
1144+
// Resolve visibility results
1145+
mOcclusionQueryPool.resolveVisibilityResults(this);
11371146
}
11381147

11391148
void ContextMtl::endEncoding(bool forceSaveRenderPassContent)
@@ -1146,7 +1155,7 @@ bool NeedToInvertDepthRange(float near, float far)
11461155
mRenderEncoder.setStoreAction(MTLStoreActionStore);
11471156
}
11481157

1149-
mRenderEncoder.endEncoding();
1158+
endRenderEncoding(&mRenderEncoder);
11501159
}
11511160

11521161
if (mBlitEncoder.valid())
@@ -1461,6 +1470,87 @@ bool NeedToInvertDepthRange(float near, float far)
14611470
onDrawFrameBufferChangedState(context, framebuffer, true);
14621471
}
14631472

1473+
angle::Result ContextMtl::onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query)
1474+
{
1475+
ASSERT(mOcclusionQuery == nullptr);
1476+
mOcclusionQuery = query;
1477+
1478+
if (mRenderEncoder.valid())
1479+
{
1480+
// if render pass has started, start the query in the encoder
1481+
return startOcclusionQueryInRenderPass(query, true);
1482+
}
1483+
else
1484+
{
1485+
query->resetVisibilityResult(this);
1486+
}
1487+
1488+
return angle::Result::Continue;
1489+
}
1490+
void ContextMtl::onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query)
1491+
{
1492+
ASSERT(mOcclusionQuery == query);
1493+
1494+
if (mRenderEncoder.valid())
1495+
{
1496+
// if render pass has started, end the query in the encoder
1497+
disableActiveOcclusionQueryInRenderPass();
1498+
}
1499+
1500+
mOcclusionQuery = nullptr;
1501+
}
1502+
void ContextMtl::onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query)
1503+
{
1504+
if (query->getAllocatedVisibilityOffsets().empty())
1505+
{
1506+
return;
1507+
}
1508+
if (mOcclusionQuery == query)
1509+
{
1510+
onOcclusionQueryEnd(context, query);
1511+
}
1512+
mOcclusionQueryPool.deallocateQueryOffset(this, query);
1513+
}
1514+
1515+
void ContextMtl::disableActiveOcclusionQueryInRenderPass()
1516+
{
1517+
if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
1518+
{
1519+
return;
1520+
}
1521+
1522+
ASSERT(mRenderEncoder.valid());
1523+
mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeDisabled,
1524+
mOcclusionQuery->getAllocatedVisibilityOffsets().back());
1525+
}
1526+
1527+
angle::Result ContextMtl::restartActiveOcclusionQueryInRenderPass()
1528+
{
1529+
if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
1530+
{
1531+
return angle::Result::Continue;
1532+
}
1533+
1534+
return startOcclusionQueryInRenderPass(mOcclusionQuery, false);
1535+
}
1536+
1537+
angle::Result ContextMtl::startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue)
1538+
{
1539+
ASSERT(mRenderEncoder.valid());
1540+
1541+
ANGLE_TRY(mOcclusionQueryPool.allocateQueryOffset(this, query, clearOldValue));
1542+
1543+
mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeBoolean,
1544+
query->getAllocatedVisibilityOffsets().back());
1545+
1546+
// We need to mark the query's buffer as being written in this command buffer now. Since the
1547+
// actual writing is deferred until the render pass ends and user could try to read the query
1548+
// result before the render pass ends.
1549+
mCmdBuffer.setWriteDependency(query->getVisibilityResultBuffer());
1550+
1551+
return angle::Result::Continue;
1552+
}
1553+
14641554
void ContextMtl::updateProgramExecutable(const gl::Context *context)
14651555
{
14661556
// Need to rebind textures
@@ -1547,6 +1637,13 @@ bool NeedToInvertDepthRange(float near, float far)
15471637
invalidateState(context);
15481638
}
15491639

1640+
if (mOcclusionQuery && mOcclusionQueryPool.getNumRenderPassAllocatedQueries() == 0)
1641+
{
1642+
// The occlusion query is still active, and a new render pass has started.
1643+
// We need to continue the querying process in the new render encoder.
1644+
ANGLE_TRY(startOcclusionQueryInRenderPass(mOcclusionQuery, false));
1645+
}
1646+
15501647
Optional<mtl::RenderPipelineDesc> changedPipelineDesc;
15511648
ANGLE_TRY(checkIfPipelineChanged(context, mode, &changedPipelineDesc));
15521649

src/libANGLE/renderer/metal/DisplayMtl.mm

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2019 The ANGLE Project Authors. All rights reserved.
2+
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
33
// Use of this source code is governed by a BSD-style license that can be
44
// found in the LICENSE file.
55
//
@@ -627,8 +627,7 @@ bool IsMetalDisplayAvailable()
627627

628628
mNativeExtensions.eglSyncOES = false;
629629

630-
// NOTE(hqle): support occlusion query
631-
mNativeExtensions.occlusionQueryBoolean = false;
630+
mNativeExtensions.occlusionQueryBoolean = true;
632631

633632
mNativeExtensions.disjointTimerQuery = false;
634633
mNativeExtensions.queryCounterBitsTimeElapsed = false;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// Copyright 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.h:
7+
// Defines the class interface for QueryMtl, implementing QueryImpl.
8+
//
9+
10+
#ifndef LIBANGLE_RENDERER_METAL_QUERYMTL_H_
11+
#define LIBANGLE_RENDERER_METAL_QUERYMTL_H_
12+
13+
#include "libANGLE/renderer/QueryImpl.h"
14+
#include "libANGLE/renderer/metal/mtl_common.h"
15+
#include "libANGLE/renderer/metal/mtl_resources.h"
16+
17+
namespace rx
18+
{
19+
20+
class ContextMtl;
21+
22+
// The class represents offset(s) allocated in the visiblity buffer for an occlusion query.
23+
// See doc/OcclusionQuery.md.
24+
// An occlusion query might have more than one offsets allocated, but all of them must be adjacent
25+
// to each other. Multiple offsets typically allocated when the query is paused and resumed during
26+
// viewport clear emulation with draw operations. In such case, Metal doesn't allow an offset to
27+
// be reused in a render pass, hence multiple offsets will be allocated, and their values will
28+
// be accumulated.
29+
class VisibilityBufferOffsetsMtl
30+
{
31+
public:
32+
void clear() { mStartOffset = mNumOffsets = 0; }
33+
bool empty() const { return mNumOffsets == 0; }
34+
uint32_t size() const { return mNumOffsets; }
35+
36+
// Return last offset
37+
uint32_t back() const
38+
{
39+
ASSERT(!empty());
40+
return mStartOffset + (mNumOffsets - 1) * mtl::kOcclusionQueryResultSize;
41+
}
42+
43+
uint32_t front() const
44+
{
45+
ASSERT(!empty());
46+
return mStartOffset;
47+
}
48+
49+
void setStartOffset(uint32_t offset)
50+
{
51+
ASSERT(empty());
52+
mStartOffset = offset;
53+
mNumOffsets = 1;
54+
}
55+
void addOffset()
56+
{
57+
ASSERT(!empty());
58+
mNumOffsets++;
59+
}
60+
61+
private:
62+
uint32_t mStartOffset = 0;
63+
uint32_t mNumOffsets = 0;
64+
};
65+
66+
class QueryMtl : public QueryImpl
67+
{
68+
public:
69+
QueryMtl(gl::QueryType type);
70+
~QueryMtl() override;
71+
72+
void onDestroy(const gl::Context *context) override;
73+
74+
angle::Result begin(const gl::Context *context) override;
75+
angle::Result end(const gl::Context *context) override;
76+
angle::Result queryCounter(const gl::Context *context) override;
77+
angle::Result getResult(const gl::Context *context, GLint *params) override;
78+
angle::Result getResult(const gl::Context *context, GLuint *params) override;
79+
angle::Result getResult(const gl::Context *context, GLint64 *params) override;
80+
angle::Result getResult(const gl::Context *context, GLuint64 *params) override;
81+
angle::Result isResultAvailable(const gl::Context *context, bool *available) override;
82+
83+
// Get allocated offsets in the render pass's occlusion query pool.
84+
const VisibilityBufferOffsetsMtl &getAllocatedVisibilityOffsets() const
85+
{
86+
return mVisibilityBufferOffsets;
87+
}
88+
// Set first allocated offset in the render pass's occlusion query pool.
89+
void setFirstAllocatedVisibilityOffset(uint32_t off)
90+
{
91+
mVisibilityBufferOffsets.setStartOffset(off);
92+
}
93+
// Add more offset allocated for the occlusion query
94+
void addAllocatedVisibilityOffset() { mVisibilityBufferOffsets.addOffset(); }
95+
96+
void clearAllocatedVisibilityOffsets() { mVisibilityBufferOffsets.clear(); }
97+
// Returns the buffer containing the final occlusion query result.
98+
const mtl::BufferRef &getVisibilityResultBuffer() const { return mVisibilityResultBuffer; }
99+
// Reset the occlusion query result stored in buffer to zero
100+
void resetVisibilityResult(ContextMtl *contextMtl);
101+
102+
private:
103+
template <typename T>
104+
angle::Result waitAndGetResult(const gl::Context *context, T *params);
105+
106+
// List of offsets in the render pass's occlusion query pool buffer allocated for this query
107+
VisibilityBufferOffsetsMtl mVisibilityBufferOffsets;
108+
mtl::BufferRef mVisibilityResultBuffer;
109+
};
110+
111+
} // namespace rx
112+
113+
#endif /* LIBANGLE_RENDERER_METAL_QUERYMTL_H_ */

0 commit comments

Comments
 (0)