Skip to content

Commit 246e8e9

Browse files
committed
Merge pull request #98803 from stuartcarnie/metal_multiview
Metal: Multiview support
2 parents b80aa65 + 3dac388 commit 246e8e9

File tree

5 files changed

+337
-188
lines changed

5 files changed

+337
-188
lines changed

drivers/metal/metal_objects.h

Lines changed: 145 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ MTL_CLASS(Texture)
8282

8383
} //namespace MTL
8484

85+
/// Metal buffer index for the view mask when rendering multi-view.
86+
const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
87+
8588
enum ShaderStageUsage : uint32_t {
8689
None = 0,
8790
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
@@ -142,6 +145,12 @@ struct ClearAttKey {
142145
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
143146
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
144147

148+
enum Flags : uint16_t {
149+
CLEAR_FLAGS_NONE = 0,
150+
CLEAR_FLAGS_LAYERED = 1 << 0,
151+
};
152+
153+
Flags flags = CLEAR_FLAGS_NONE;
145154
uint16_t sample_count = 0;
146155
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
147156

@@ -150,19 +159,22 @@ struct ClearAttKey {
150159
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
151160
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
152161
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
162+
_FORCE_INLINE_ void enable_layered_rendering() { flags::set(flags, CLEAR_FLAGS_LAYERED); }
153163

154164
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
155165
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
156166
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
167+
_FORCE_INLINE_ bool is_layered_rendering_enabled() const { return flags::any(flags, CLEAR_FLAGS_LAYERED); }
157168

158169
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
159170
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
160171
}
161172

162173
uint32_t hash() const {
163-
uint32_t h = hash_murmur3_one_32(sample_count);
174+
uint32_t h = hash_murmur3_one_32(flags);
175+
h = hash_murmur3_one_32(sample_count, h);
164176
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
165-
return h;
177+
return hash_fmix32(h);
166178
}
167179
};
168180

@@ -206,6 +218,97 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceCache {
206218
~MDResourceCache() = default;
207219
};
208220

221+
enum class MDAttachmentType : uint8_t {
222+
None = 0,
223+
Color = 1 << 0,
224+
Depth = 1 << 1,
225+
Stencil = 1 << 2,
226+
};
227+
228+
_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
229+
flags::set(p_a, p_b);
230+
return p_a;
231+
}
232+
233+
_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
234+
return uint8_t(p_a) & uint8_t(p_b);
235+
}
236+
237+
struct MDSubpass {
238+
uint32_t subpass_index = 0;
239+
uint32_t view_count = 0;
240+
LocalVector<RDD::AttachmentReference> input_references;
241+
LocalVector<RDD::AttachmentReference> color_references;
242+
RDD::AttachmentReference depth_stencil_reference;
243+
LocalVector<RDD::AttachmentReference> resolve_references;
244+
245+
MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
246+
};
247+
248+
struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
249+
private:
250+
uint32_t index = 0;
251+
uint32_t firstUseSubpassIndex = 0;
252+
uint32_t lastUseSubpassIndex = 0;
253+
254+
public:
255+
MTLPixelFormat format = MTLPixelFormatInvalid;
256+
MDAttachmentType type = MDAttachmentType::None;
257+
MTLLoadAction loadAction = MTLLoadActionDontCare;
258+
MTLStoreAction storeAction = MTLStoreActionDontCare;
259+
MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
260+
MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
261+
uint32_t samples = 1;
262+
263+
/*!
264+
* @brief Returns true if this attachment is first used in the given subpass.
265+
* @param p_subpass
266+
* @return
267+
*/
268+
_FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
269+
return p_subpass.subpass_index == firstUseSubpassIndex;
270+
}
271+
272+
/*!
273+
* @brief Returns true if this attachment is last used in the given subpass.
274+
* @param p_subpass
275+
* @return
276+
*/
277+
_FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
278+
return p_subpass.subpass_index == lastUseSubpassIndex;
279+
}
280+
281+
void linkToSubpass(MDRenderPass const &p_pass);
282+
283+
MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
284+
bool p_is_rendering_entire_area,
285+
bool p_has_resolve,
286+
bool p_can_resolve,
287+
bool p_is_stencil) const;
288+
bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
289+
PixelFormats &p_pf,
290+
MDSubpass const &p_subpass,
291+
id<MTLTexture> p_attachment,
292+
bool p_is_rendering_entire_area,
293+
bool p_has_resolve,
294+
bool p_can_resolve,
295+
bool p_is_stencil) const;
296+
/** Returns whether this attachment should be cleared in the subpass. */
297+
bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
298+
};
299+
300+
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
301+
public:
302+
Vector<MDAttachment> attachments;
303+
Vector<MDSubpass> subpasses;
304+
305+
uint32_t get_sample_count() const {
306+
return attachments.is_empty() ? 1 : attachments[0].samples;
307+
}
308+
309+
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
310+
};
311+
209312
class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
210313
private:
211314
RenderingDeviceDriverMetal *device_driver = nullptr;
@@ -220,8 +323,8 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
220323
void _render_set_dirty_state();
221324
void _render_bind_uniform_sets();
222325

223-
static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
224-
static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
326+
void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
327+
uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
225328
void _end_render_pass();
226329
void _render_clear_render_area();
227330

@@ -268,34 +371,14 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
268371
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
269372
uint64_t uniform_set_mask = 0;
270373

271-
_FORCE_INLINE_ void reset() {
272-
pass = nil;
273-
frameBuffer = nil;
274-
pipeline = nil;
275-
current_subpass = UINT32_MAX;
276-
render_area = {};
277-
is_rendering_entire_area = false;
278-
desc = nil;
279-
encoder = nil;
280-
index_buffer = nil;
281-
index_type = MTLIndexTypeUInt16;
282-
dirty = DIRTY_NONE;
283-
uniform_sets.clear();
284-
uniform_set_mask = 0;
285-
clear_values.clear();
286-
viewports.clear();
287-
scissors.clear();
288-
blend_constants.reset();
289-
vertex_buffers.clear();
290-
vertex_offsets.clear();
291-
// Keep the keys, as they are likely to be used again.
292-
for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
293-
kv.value.clear();
294-
}
295-
}
296-
374+
_FORCE_INLINE_ void reset();
297375
void end_encoding();
298376

377+
_ALWAYS_INLINE_ const MDSubpass &get_subpass() const {
378+
DEV_ASSERT(pass != nullptr);
379+
return pass->subpasses[current_subpass];
380+
}
381+
299382
_FORCE_INLINE_ void mark_viewport_dirty() {
300383
if (viewports.is_empty()) {
301384
return;
@@ -649,6 +732,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderShader final : public MDShad
649732
uint32_t size = 0;
650733
} frag;
651734
} push_constants;
735+
bool needs_view_mask_buffer = false;
652736

653737
MDLibrary *vert;
654738
MDLibrary *frag;
@@ -659,7 +743,10 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderShader final : public MDShad
659743

660744
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
661745

662-
MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *p_vert, MDLibrary *p_frag);
746+
MDRenderShader(CharString p_name,
747+
bool p_needs_view_mask_buffer,
748+
Vector<UniformSet> p_sets,
749+
MDLibrary *p_vert, MDLibrary *p_frag);
663750
};
664751

665752
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
@@ -702,96 +789,6 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
702789
BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device);
703790
};
704791

705-
enum class MDAttachmentType : uint8_t {
706-
None = 0,
707-
Color = 1 << 0,
708-
Depth = 1 << 1,
709-
Stencil = 1 << 2,
710-
};
711-
712-
_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
713-
flags::set(p_a, p_b);
714-
return p_a;
715-
}
716-
717-
_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
718-
return uint8_t(p_a) & uint8_t(p_b);
719-
}
720-
721-
struct MDSubpass {
722-
uint32_t subpass_index = 0;
723-
LocalVector<RDD::AttachmentReference> input_references;
724-
LocalVector<RDD::AttachmentReference> color_references;
725-
RDD::AttachmentReference depth_stencil_reference;
726-
LocalVector<RDD::AttachmentReference> resolve_references;
727-
728-
MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
729-
};
730-
731-
struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
732-
private:
733-
uint32_t index = 0;
734-
uint32_t firstUseSubpassIndex = 0;
735-
uint32_t lastUseSubpassIndex = 0;
736-
737-
public:
738-
MTLPixelFormat format = MTLPixelFormatInvalid;
739-
MDAttachmentType type = MDAttachmentType::None;
740-
MTLLoadAction loadAction = MTLLoadActionDontCare;
741-
MTLStoreAction storeAction = MTLStoreActionDontCare;
742-
MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
743-
MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
744-
uint32_t samples = 1;
745-
746-
/*!
747-
* @brief Returns true if this attachment is first used in the given subpass.
748-
* @param p_subpass
749-
* @return
750-
*/
751-
_FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
752-
return p_subpass.subpass_index == firstUseSubpassIndex;
753-
}
754-
755-
/*!
756-
* @brief Returns true if this attachment is last used in the given subpass.
757-
* @param p_subpass
758-
* @return
759-
*/
760-
_FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
761-
return p_subpass.subpass_index == lastUseSubpassIndex;
762-
}
763-
764-
void linkToSubpass(MDRenderPass const &p_pass);
765-
766-
MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
767-
bool p_is_rendering_entire_area,
768-
bool p_has_resolve,
769-
bool p_can_resolve,
770-
bool p_is_stencil) const;
771-
bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
772-
PixelFormats &p_pf,
773-
MDSubpass const &p_subpass,
774-
id<MTLTexture> p_attachment,
775-
bool p_is_rendering_entire_area,
776-
bool p_has_resolve,
777-
bool p_can_resolve,
778-
bool p_is_stencil) const;
779-
/** Returns whether this attachment should be cleared in the subpass. */
780-
bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
781-
};
782-
783-
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
784-
public:
785-
Vector<MDAttachment> attachments;
786-
Vector<MDSubpass> subpasses;
787-
788-
uint32_t get_sample_count() const {
789-
return attachments.is_empty() ? 1 : attachments[0].samples;
790-
}
791-
792-
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
793-
};
794-
795792
class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline {
796793
public:
797794
MDPipelineType type;
@@ -892,13 +889,39 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputePipeline final : public MDP
892889
};
893890

894891
class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer {
895-
public:
896892
Vector<MTL::Texture> textures;
893+
894+
public:
897895
Size2i size;
898896
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
899897
textures(p_textures), size(p_size) {}
900898
MDFrameBuffer() {}
901899

900+
/// Returns the texture at the given index.
901+
_ALWAYS_INLINE_ MTL::Texture get_texture(uint32_t p_idx) const {
902+
return textures[p_idx];
903+
}
904+
905+
/// Returns true if the texture at the given index is not nil.
906+
_ALWAYS_INLINE_ bool has_texture(uint32_t p_idx) const {
907+
return textures[p_idx] != nil;
908+
}
909+
910+
/// Set the texture at the given index.
911+
_ALWAYS_INLINE_ void set_texture(uint32_t p_idx, MTL::Texture p_texture) {
912+
textures.write[p_idx] = p_texture;
913+
}
914+
915+
/// Unset or nil the texture at the given index.
916+
_ALWAYS_INLINE_ void unset_texture(uint32_t p_idx) {
917+
textures.write[p_idx] = nil;
918+
}
919+
920+
/// Resizes buffers to the specified size.
921+
_ALWAYS_INLINE_ void set_texture_count(uint32_t p_size) {
922+
textures.resize(p_size);
923+
}
924+
902925
virtual ~MDFrameBuffer() = default;
903926
};
904927

0 commit comments

Comments
 (0)