|
@@ -96,6 +96,9 @@ void MDCommandBuffer::bind_pipeline(RDD::PipelineID p_pipeline) {
|
|
|
MDRenderPipeline *rp = (MDRenderPipeline *)p;
|
|
|
|
|
|
if (render.encoder == nil) {
|
|
|
+ // This error would happen if the render pass failed.
|
|
|
+ ERR_FAIL_NULL_MSG(render.desc, "Render pass descriptor is null.");
|
|
|
+
|
|
|
// This condition occurs when there are no attachments when calling render_next_subpass()
|
|
|
// and is due to the SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS flag.
|
|
|
render.desc.defaultRasterSampleCount = static_cast<NSUInteger>(rp->sample_count);
|
|
@@ -223,8 +226,9 @@ void MDCommandBuffer::render_bind_uniform_set(RDD::UniformSetID p_uniform_set, R
|
|
|
void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) {
|
|
|
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
|
|
|
- uint32_t vertex_count = p_rects.size() * 6;
|
|
|
+ const MDSubpass &subpass = render.get_subpass();
|
|
|
|
|
|
+ uint32_t vertex_count = p_rects.size() * 6 * subpass.view_count;
|
|
|
simd::float4 vertices[vertex_count];
|
|
|
simd::float4 clear_colors[ClearAttKey::ATTACHMENT_COUNT];
|
|
|
|
|
@@ -235,6 +239,9 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
|
|
|
|
|
|
ClearAttKey key;
|
|
|
key.sample_count = render.pass->get_sample_count();
|
|
|
+ if (subpass.view_count > 1) {
|
|
|
+ key.enable_layered_rendering();
|
|
|
+ }
|
|
|
|
|
|
float depth_value = 0;
|
|
|
uint32_t stencil_value = 0;
|
|
@@ -245,7 +252,7 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
|
|
|
if (attClear.aspect.has_flag(RDD::TEXTURE_ASPECT_COLOR_BIT)) {
|
|
|
attachment_index = attClear.color_attachment;
|
|
|
} else {
|
|
|
- attachment_index = render.pass->subpasses[render.current_subpass].depth_stencil_reference.attachment;
|
|
|
+ attachment_index = subpass.depth_stencil_reference.attachment;
|
|
|
}
|
|
|
|
|
|
MDAttachment const &mda = render.pass->attachments[attachment_index];
|
|
@@ -310,6 +317,13 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
|
|
|
void MDCommandBuffer::_render_set_dirty_state() {
|
|
|
_render_bind_uniform_sets();
|
|
|
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
+ if (subpass.view_count > 1) {
|
|
|
+ uint32_t view_range[2] = { 0, subpass.view_count };
|
|
|
+ [render.encoder setVertexBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX];
|
|
|
+ [render.encoder setFragmentBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX];
|
|
|
+ }
|
|
|
+
|
|
|
if (render.dirty.has_flag(RenderState::DIRTY_PIPELINE)) {
|
|
|
[render.encoder setRenderPipelineState:render.pipeline->state];
|
|
|
}
|
|
@@ -492,36 +506,40 @@ uint32_t MDCommandBuffer::_populate_vertices(simd::float4 *p_vertices, uint32_t
|
|
|
simd::float4 vtx;
|
|
|
|
|
|
uint32_t idx = p_index;
|
|
|
- vtx.z = 0.0;
|
|
|
- vtx.w = (float)1;
|
|
|
+ uint32_t endLayer = render.get_subpass().view_count;
|
|
|
+
|
|
|
+ for (uint32_t layer = 0; layer < endLayer; layer++) {
|
|
|
+ vtx.z = 0.0;
|
|
|
+ vtx.w = (float)layer;
|
|
|
|
|
|
- // Top left vertex - First triangle.
|
|
|
- vtx.y = topPos;
|
|
|
- vtx.x = leftPos;
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Top left vertex - First triangle.
|
|
|
+ vtx.y = topPos;
|
|
|
+ vtx.x = leftPos;
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
|
|
|
- // Bottom left vertex.
|
|
|
- vtx.y = bottomPos;
|
|
|
- vtx.x = leftPos;
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Bottom left vertex.
|
|
|
+ vtx.y = bottomPos;
|
|
|
+ vtx.x = leftPos;
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
|
|
|
- // Bottom right vertex.
|
|
|
- vtx.y = bottomPos;
|
|
|
- vtx.x = rightPos;
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Bottom right vertex.
|
|
|
+ vtx.y = bottomPos;
|
|
|
+ vtx.x = rightPos;
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
|
|
|
- // Bottom right vertex - Second triangle.
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Bottom right vertex - Second triangle.
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
|
|
|
- // Top right vertex.
|
|
|
- vtx.y = topPos;
|
|
|
- vtx.x = rightPos;
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Top right vertex.
|
|
|
+ vtx.y = topPos;
|
|
|
+ vtx.x = rightPos;
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
|
|
|
- // Top left vertex.
|
|
|
- vtx.y = topPos;
|
|
|
- vtx.x = leftPos;
|
|
|
- p_vertices[idx++] = vtx;
|
|
|
+ // Top left vertex.
|
|
|
+ vtx.y = topPos;
|
|
|
+ vtx.x = leftPos;
|
|
|
+ p_vertices[idx++] = vtx;
|
|
|
+ }
|
|
|
|
|
|
return idx;
|
|
|
}
|
|
@@ -548,8 +566,7 @@ void MDCommandBuffer::render_begin_pass(RDD::RenderPassID p_render_pass, RDD::Fr
|
|
|
|
|
|
void MDCommandBuffer::_end_render_pass() {
|
|
|
MDFrameBuffer const &fb_info = *render.frameBuffer;
|
|
|
- MDRenderPass const &pass_info = *render.pass;
|
|
|
- MDSubpass const &subpass = pass_info.subpasses[render.current_subpass];
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
|
|
|
PixelFormats &pf = device_driver->get_pixel_formats();
|
|
|
|
|
@@ -557,11 +574,11 @@ void MDCommandBuffer::_end_render_pass() {
|
|
|
uint32_t color_index = subpass.color_references[i].attachment;
|
|
|
uint32_t resolve_index = subpass.resolve_references[i].attachment;
|
|
|
DEV_ASSERT((color_index == RDD::AttachmentReference::UNUSED) == (resolve_index == RDD::AttachmentReference::UNUSED));
|
|
|
- if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.textures[color_index]) {
|
|
|
+ if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.has_texture(color_index)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- id<MTLTexture> resolve_tex = fb_info.textures[resolve_index];
|
|
|
+ id<MTLTexture> resolve_tex = fb_info.get_texture(resolve_index);
|
|
|
|
|
|
CRASH_COND_MSG(!flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve), "not implemented: unresolvable texture types");
|
|
|
// see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407
|
|
@@ -572,7 +589,7 @@ void MDCommandBuffer::_end_render_pass() {
|
|
|
|
|
|
void MDCommandBuffer::_render_clear_render_area() {
|
|
|
MDRenderPass const &pass = *render.pass;
|
|
|
- MDSubpass const &subpass = pass.subpasses[render.current_subpass];
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
|
|
|
// First determine attachments that should be cleared.
|
|
|
LocalVector<RDD::AttachmentClear> clears;
|
|
@@ -619,9 +636,14 @@ void MDCommandBuffer::render_next_subpass() {
|
|
|
|
|
|
MDFrameBuffer const &fb = *render.frameBuffer;
|
|
|
MDRenderPass const &pass = *render.pass;
|
|
|
- MDSubpass const &subpass = pass.subpasses[render.current_subpass];
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
|
|
|
MTLRenderPassDescriptor *desc = MTLRenderPassDescriptor.renderPassDescriptor;
|
|
|
+
|
|
|
+ if (subpass.view_count > 1) {
|
|
|
+ desc.renderTargetArrayLength = subpass.view_count;
|
|
|
+ }
|
|
|
+
|
|
|
PixelFormats &pf = device_driver->get_pixel_formats();
|
|
|
|
|
|
uint32_t attachmentCount = 0;
|
|
@@ -638,7 +660,7 @@ void MDCommandBuffer::render_next_subpass() {
|
|
|
bool has_resolve = resolveIdx != RDD::AttachmentReference::UNUSED;
|
|
|
bool can_resolve = true;
|
|
|
if (resolveIdx != RDD::AttachmentReference::UNUSED) {
|
|
|
- id<MTLTexture> resolve_tex = fb.textures[resolveIdx];
|
|
|
+ id<MTLTexture> resolve_tex = fb.get_texture(resolveIdx);
|
|
|
can_resolve = flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve);
|
|
|
if (can_resolve) {
|
|
|
ca.resolveTexture = resolve_tex;
|
|
@@ -649,7 +671,9 @@ void MDCommandBuffer::render_next_subpass() {
|
|
|
|
|
|
MDAttachment const &attachment = pass.attachments[idx];
|
|
|
|
|
|
- id<MTLTexture> tex = fb.textures[idx];
|
|
|
+ id<MTLTexture> tex = fb.get_texture(idx);
|
|
|
+ ERR_FAIL_NULL_MSG(tex, "Frame buffer color texture is null.");
|
|
|
+
|
|
|
if ((attachment.type & MDAttachmentType::Color)) {
|
|
|
if (attachment.configureDescriptor(ca, pf, subpass, tex, render.is_rendering_entire_area, has_resolve, can_resolve, false)) {
|
|
|
Color clearColor = render.clear_values[idx].color;
|
|
@@ -662,7 +686,8 @@ void MDCommandBuffer::render_next_subpass() {
|
|
|
attachmentCount += 1;
|
|
|
uint32_t idx = subpass.depth_stencil_reference.attachment;
|
|
|
MDAttachment const &attachment = pass.attachments[idx];
|
|
|
- id<MTLTexture> tex = fb.textures[idx];
|
|
|
+ id<MTLTexture> tex = fb.get_texture(idx);
|
|
|
+ ERR_FAIL_NULL_MSG(tex, "Frame buffer depth / stencil texture is null.");
|
|
|
if (attachment.type & MDAttachmentType::Depth) {
|
|
|
MTLRenderPassDepthAttachmentDescriptor *da = desc.depthAttachment;
|
|
|
if (attachment.configureDescriptor(da, pf, subpass, tex, render.is_rendering_entire_area, false, false, false)) {
|
|
@@ -702,8 +727,15 @@ void MDCommandBuffer::render_draw(uint32_t p_vertex_count,
|
|
|
uint32_t p_base_vertex,
|
|
|
uint32_t p_first_instance) {
|
|
|
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
|
|
|
+
|
|
|
_render_set_dirty_state();
|
|
|
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
+ if (subpass.view_count > 1) {
|
|
|
+ p_instance_count *= subpass.view_count;
|
|
|
+ }
|
|
|
+
|
|
|
DEV_ASSERT(render.dirty == 0);
|
|
|
|
|
|
id<MTLRenderCommandEncoder> enc = render.encoder;
|
|
@@ -751,8 +783,15 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count,
|
|
|
int32_t p_vertex_offset,
|
|
|
uint32_t p_first_instance) {
|
|
|
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
|
|
|
+
|
|
|
_render_set_dirty_state();
|
|
|
|
|
|
+ MDSubpass const &subpass = render.get_subpass();
|
|
|
+ if (subpass.view_count > 1) {
|
|
|
+ p_instance_count *= subpass.view_count;
|
|
|
+ }
|
|
|
+
|
|
|
id<MTLRenderCommandEncoder> enc = render.encoder;
|
|
|
|
|
|
uint32_t index_offset = render.index_offset;
|
|
@@ -770,6 +809,8 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count,
|
|
|
|
|
|
void MDCommandBuffer::render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) {
|
|
|
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
|
|
|
+
|
|
|
_render_set_dirty_state();
|
|
|
|
|
|
id<MTLRenderCommandEncoder> enc = render.encoder;
|
|
@@ -794,6 +835,8 @@ void MDCommandBuffer::render_draw_indexed_indirect_count(RDD::BufferID p_indirec
|
|
|
|
|
|
void MDCommandBuffer::render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) {
|
|
|
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
|
|
|
+
|
|
|
_render_set_dirty_state();
|
|
|
|
|
|
id<MTLRenderCommandEncoder> enc = render.encoder;
|
|
@@ -813,6 +856,42 @@ void MDCommandBuffer::render_draw_indirect_count(RDD::BufferID p_indirect_buffer
|
|
|
ERR_FAIL_MSG("not implemented");
|
|
|
}
|
|
|
|
|
|
+void MDCommandBuffer::render_end_pass() {
|
|
|
+ DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
+
|
|
|
+ render.end_encoding();
|
|
|
+ render.reset();
|
|
|
+ type = MDCommandBufferStateType::None;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - RenderState
|
|
|
+
|
|
|
+void MDCommandBuffer::RenderState::reset() {
|
|
|
+ pass = nil;
|
|
|
+ frameBuffer = nil;
|
|
|
+ pipeline = nil;
|
|
|
+ current_subpass = UINT32_MAX;
|
|
|
+ render_area = {};
|
|
|
+ is_rendering_entire_area = false;
|
|
|
+ desc = nil;
|
|
|
+ encoder = nil;
|
|
|
+ index_buffer = nil;
|
|
|
+ index_type = MTLIndexTypeUInt16;
|
|
|
+ dirty = DIRTY_NONE;
|
|
|
+ uniform_sets.clear();
|
|
|
+ uniform_set_mask = 0;
|
|
|
+ clear_values.clear();
|
|
|
+ viewports.clear();
|
|
|
+ scissors.clear();
|
|
|
+ blend_constants.reset();
|
|
|
+ vertex_buffers.clear();
|
|
|
+ vertex_offsets.clear();
|
|
|
+ // Keep the keys, as they are likely to be used again.
|
|
|
+ for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
|
|
|
+ kv.value.clear();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void MDCommandBuffer::RenderState::end_encoding() {
|
|
|
if (encoder == nil) {
|
|
|
return;
|
|
@@ -842,6 +921,8 @@ void MDCommandBuffer::RenderState::end_encoding() {
|
|
|
encoder = nil;
|
|
|
}
|
|
|
|
|
|
+#pragma mark - ComputeState
|
|
|
+
|
|
|
void MDCommandBuffer::ComputeState::end_encoding() {
|
|
|
if (encoder == nil) {
|
|
|
return;
|
|
@@ -862,14 +943,6 @@ void MDCommandBuffer::ComputeState::end_encoding() {
|
|
|
encoder = nil;
|
|
|
}
|
|
|
|
|
|
-void MDCommandBuffer::render_end_pass() {
|
|
|
- DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
|
|
-
|
|
|
- render.end_encoding();
|
|
|
- render.reset();
|
|
|
- type = MDCommandBufferStateType::None;
|
|
|
-}
|
|
|
-
|
|
|
#pragma mark - Compute
|
|
|
|
|
|
void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index) {
|
|
@@ -943,8 +1016,11 @@ void MDComputeShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDC
|
|
|
[enc setBytes:ptr length:length atIndex:push_constants.binding];
|
|
|
}
|
|
|
|
|
|
-MDRenderShader::MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) :
|
|
|
- MDShader(p_name, p_sets), vert(p_vert), frag(p_frag) {
|
|
|
+MDRenderShader::MDRenderShader(CharString p_name,
|
|
|
+ bool p_needs_view_mask_buffer,
|
|
|
+ Vector<UniformSet> p_sets,
|
|
|
+ MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) :
|
|
|
+ MDShader(p_name, p_sets), needs_view_mask_buffer(p_needs_view_mask_buffer), vert(p_vert), frag(p_frag) {
|
|
|
}
|
|
|
|
|
|
void MDRenderShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) {
|
|
@@ -1279,7 +1355,7 @@ typedef struct {
|
|
|
|
|
|
typedef struct {
|
|
|
float4 v_position [[position]];
|
|
|
- uint layer;
|
|
|
+ uint layer%s;
|
|
|
} VaryingsPos;
|
|
|
|
|
|
vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant ClearColorsIn& ccIn [[buffer(0)]]) {
|
|
@@ -1288,7 +1364,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
|
|
|
varyings.layer = uint(attributes.a_position.w);
|
|
|
return varyings;
|
|
|
}
|
|
|
-)", ClearAttKey::DEPTH_INDEX];
|
|
|
+)", p_key.is_layered_rendering_enabled() ? " [[render_target_array_index]]" : "", ClearAttKey::DEPTH_INDEX];
|
|
|
|
|
|
return new_func(msl, @"vertClear", nil);
|
|
|
}
|
|
@@ -1578,7 +1654,7 @@ void ShaderCacheEntry::notify_free() const {
|
|
|
self->_library = library;
|
|
|
self->_error = error;
|
|
|
if (error) {
|
|
|
- ERR_PRINT(String(U"Error compiling shader %s: %s").format(entry->name.get_data(), error.localizedDescription.UTF8String));
|
|
|
+ ERR_PRINT(vformat(U"Error compiling shader %s: %s", entry->name.get_data(), error.localizedDescription.UTF8String));
|
|
|
}
|
|
|
|
|
|
{
|