Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metal: Add support for 2017 era iOS devices #99820

Merged
merged 1 commit into from
Dec 20, 2024

Conversation

stuartcarnie
Copy link
Contributor

@stuartcarnie stuartcarnie commented Nov 29, 2024

Closes #99682

Add support for Apple4 GPUs.

Tier 2 argument buffers are required for read / write texture support, specifically for iOS, so this PR introduces support for generating shaders and binding without argument buffers for devices that only support Tier 1 argument buffers.

Important

I do not have an Apple4 GPU to test on, but I added an environment variable to allow me to force the shaders to be compiles without argument buffer support and was able to render the editor and complex scenes like the Bistro Demo.

SPIRV-Cross Update

SPIRV-Cross is also updated to the latest, which includes fixes and support for mesh shaders, which will be used for future features.

@bruvzg

This comment was marked as outdated.

@akien-mga

This comment was marked as outdated.

@TCROC

This comment was marked as outdated.

@stuartcarnie

This comment was marked as outdated.

@TCROC

This comment was marked as outdated.

@stuartcarnie

This comment was marked as outdated.

@stuartcarnie stuartcarnie requested a review from a team as a code owner December 15, 2024 18:28
@stuartcarnie stuartcarnie changed the title Metal: Ensure argument buffer tier 2 is specified Metal: Add support for 2017 era iOS devices Dec 15, 2024
@stuartcarnie stuartcarnie force-pushed the issue_99682 branch 3 times, most recently from edc5a55 to 0df4009 Compare December 16, 2024 13:24
@stuartcarnie stuartcarnie self-assigned this Dec 16, 2024
@TCROC
Copy link
Contributor

TCROC commented Dec 16, 2024

@stuartcarnie Does this PR also fall back to vulkan for iOS devices older than 2017?

@TCROC
Copy link
Contributor

TCROC commented Dec 16, 2024

Or is there a separate work in progress for that?

@stuartcarnie
Copy link
Contributor Author

Vulkan won't work on devices prior to 2017 either, as Godot requires image cube map textures, which are only supported on 2017 devices and beyond.

@TCROC
Copy link
Contributor

TCROC commented Dec 16, 2024

Gotcha. Does it fall back to compatibility from metal then?

@stuartcarnie
Copy link
Contributor Author

To put it another way, there should be no reason to require Vulkan / MoltenVK over Metal for Apple mobile devices, as they support the same range of devices.

For desktop, Vulkan / MoltenVK is required for x86 hardware.

@stuartcarnie
Copy link
Contributor Author

Gotcha. Does it fall back to compatibility from metal then?

Not sure – would have to ask the rendering team if that is the case.

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thirdparty changes seem fine to me, and yay for more hardware support!

@@ -61,7 +61,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : public

uint32_t version_major = 2;
uint32_t version_minor = 0;
MetalDeviceProperties *metal_device_properties = nullptr;
MetalDeviceProperties *device_properties = nullptr;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the metal_ prefix, as it is obviously for metal 🤦🏻

@@ -1058,7 +1058,7 @@ static const API_AVAILABLE(macos(11.0), ios(14.0)) MTLSamplerBorderColor SAMPLER

#pragma mark - Shader

const uint32_t SHADER_BINARY_VERSION = 3;
const uint32_t SHADER_BINARY_VERSION = 4;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to include a flag to indicate whether the shader was compiled to generate an ABI with argument buffers or classic binding model.

Comment on lines -1546 to -1547
uint32_t is_compute = UINT32_MAX;
uint32_t needs_view_mask_buffer = UINT32_MAX;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped is_compute as we don't need to store it, and moved any boolean flags to a single uint32_t, so it is easier to add them in the future.

Comment on lines +2039 to +2041
if (String v = OS::get_singleton()->get_environment(U"GODOT_DISABLE_ARGUMENT_BUFFERS"); v == U"1") {
disable_argument_buffers = true;
}
Copy link
Contributor Author

@stuartcarnie stuartcarnie Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GODOT_DISABLE_ARGUMENT_BUFFERS environment variable is an undocumented feature I use for debugging purposes. I was able to test out the classic ABI binding (no argument buffers) for a number of projects and they ran without issue

Comment on lines +2093 to +2098
std::string source;
try {
source = compiler.compile();
} catch (CompilerError &e) {
ERR_FAIL_V_MSG(Result(), "Failed to compile stage " + String(SHADER_STAGE_NAMES[stage]) + ": " + e.what());
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible that compile will throw an exception

@@ -2088,8 +2141,8 @@ void deserialize(BufReader &p_reader) {
return res;
};

auto descriptor_bindings = [&compiler, &active, &uniform_sets, stage, &get_decoration](SmallVector<Resource> &resources, Writable writable) {
for (Resource const &res : resources) {
auto descriptor_bindings = [&compiler, &active, &uniform_sets, stage, &get_decoration](SmallVector<Resource> &p_resources, Writable p_writable) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the arguments of the lambda to include the p_ prefix

Comment on lines +2440 to +2444
// We need to regenerate the shader if the cache is moved to an incompatible device.
ERR_FAIL_COND_V_MSG(device_properties->features.argument_buffers_tier < MTLArgumentBuffersTier2 && binary_data.uses_argument_buffers(),
ShaderID(),
"Shader was generated with argument buffers, but device has limited support");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures that if someone copies the Godot shader_cache to a device that supports a lower argument buffer tier, that it won't crash, but regenerate the shaders.

@@ -249,7 +249,7 @@
const MDSubpass &subpass = render.get_subpass();

uint32_t vertex_count = p_rects.size() * 6 * subpass.view_count;
simd::float4 vertices[vertex_count];
simd::float4 *vertices = ALLOCA_ARRAY(simd::float4, vertex_count);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying compiler warning in my IDE, so I switched to alloca

@@ -1093,9 +1047,373 @@
}
}

BoundUniformSet &MDUniformSet::boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device) {
void MDUniformSet::bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::RenderState &p_state) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the original argument buffers implementation moved to a new method, so we can call the right binding, depending on whether the shader uses argument buffer ABI or classic ABI

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me. I don't know much about the Metal API and I don't have a 2017-era device to test with, so TIWAGOS

@Repiteo Repiteo merged commit effea56 into godotengine:master Dec 20, 2024
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Dec 20, 2024

Thanks!

@stuartcarnie stuartcarnie deleted the issue_99682 branch December 20, 2024 13:36
@TCROC
Copy link
Contributor

TCROC commented Dec 23, 2024

@stuartcarnie I got around to testing this in my fork. It appears to fix the black screen! However, I encountered this new error:

validateComputeFunctionArguments:1083: failed assertion `Compute Function(main0): argument src_particles[0] from buffer(4) with offset(0) and length(16) has space for 16 bytes, but argument has a length(128).'

It doesn't seem to have any impact that I can yet notice. It may be something I did wrong in my fork as well. I'll test with vanilla godot master to see if anything changes. Thank you again for all of the hard work you did on this!

@stuartcarnie
Copy link
Contributor Author

@TCROC thanks for reporting your issue; I've created #100764 to track it, as I have reproduced it. Fix coming soon.

@TCROC
Copy link
Contributor

TCROC commented Dec 23, 2024

Thanks @stuartcarnie ! I look forward to testing it out! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iOS Mobile Renderer Black Screen
6 participants