diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index 55abfb79f63..400cf4fad2f 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -231,30 +231,23 @@ class GHOST_DeviceVK { device_features.drawIndirectFirstInstance = VK_TRUE; device_features.fragmentStoresAndAtomics = VK_TRUE; - VkPhysicalDeviceVulkan12Features device_12_features = {}; - device_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - device_12_features.shaderOutputLayer = VK_TRUE; - device_12_features.shaderOutputViewportIndex = VK_TRUE; + /* Enable shader draw parameters on logical device when supported on physical device. */ + VkPhysicalDeviceShaderDrawParametersFeatures shader_draw_parameters = {}; + shader_draw_parameters.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES; + shader_draw_parameters.shaderDrawParameters = features_11.shaderDrawParameters; VkPhysicalDeviceMaintenance4FeaturesKHR maintenance_4 = {}; maintenance_4.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR; maintenance_4.maintenance4 = VK_TRUE; /* Mainenance4 is core in Vulkan 1.3 so we need to query for availability. */ if (has_extensions({VK_KHR_MAINTENANCE_4_EXTENSION_NAME})) { - maintenance_4.pNext = device_12_features.pNext; - device_12_features.pNext = &maintenance_4; + maintenance_4.pNext = shader_draw_parameters.pNext; + shader_draw_parameters.pNext = &maintenance_4; } - /* Enable shader draw parameters on logical device when supported on physical device. */ - VkPhysicalDeviceShaderDrawParametersFeatures shader_draw_parameters = {}; - shader_draw_parameters.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES; - shader_draw_parameters.shaderDrawParameters = features_11.shaderDrawParameters; - shader_draw_parameters.pNext = device_12_features.pNext; - device_12_features.pNext = &shader_draw_parameters; - VkDeviceCreateInfo device_create_info = {}; - device_create_info.pNext = &device_12_features; + device_create_info.pNext = &shader_draw_parameters; device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size()); device_create_info.pQueueCreateInfos = queue_create_infos.data(); diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc index de3867580cf..50ca138bdf5 100644 --- a/source/blender/gpu/vulkan/vk_backend.cc +++ b/source/blender/gpu/vulkan/vk_backend.cc @@ -81,6 +81,25 @@ void VKBackend::detect_workarounds(VKDevice &device) { VKWorkarounds workarounds; + if (G.debug & G_DEBUG_GPU_FORCE_WORKAROUNDS) { + printf("\n"); + printf("VK: Forcing workaround usage and disabling features and extensions.\n"); + printf(" Vendor: %s\n", device.vendor_name().c_str()); + printf(" Device: %s\n", device.physical_device_properties_get().deviceName); + printf(" Driver: %s\n", device.driver_version().c_str()); + /* Force workarounds. */ + workarounds.not_aligned_pixel_formats = true; + workarounds.shader_output_layer = true; + workarounds.shader_output_viewport_index = true; + + return; + } + + workarounds.shader_output_layer = + !device.physical_device_vulkan_12_features_get().shaderOutputLayer; + workarounds.shader_output_viewport_index = + !device.physical_device_vulkan_12_features_get().shaderOutputViewportIndex; + /* AMD GPUs don't support texture formats that use are aligned to 24 or 48 bits. */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { workarounds.not_aligned_pixel_formats = true; diff --git a/source/blender/gpu/vulkan/vk_device.cc b/source/blender/gpu/vulkan/vk_device.cc index 67ed6ac8d5e..1f3a1d91681 100644 --- a/source/blender/gpu/vulkan/vk_device.cc +++ b/source/blender/gpu/vulkan/vk_device.cc @@ -89,11 +89,17 @@ void VKDevice::init_physical_device_properties() void VKDevice::init_physical_device_features() { BLI_assert(vk_physical_device_ != VK_NULL_HANDLE); + VkPhysicalDeviceFeatures2 features = {}; features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; vk_physical_device_vulkan_11_features_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vk_physical_device_vulkan_12_features_.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features.pNext = &vk_physical_device_vulkan_11_features_; + vk_physical_device_vulkan_11_features_.pNext = &vk_physical_device_vulkan_12_features_; + vkGetPhysicalDeviceFeatures2(vk_physical_device_, &features); vk_physical_device_features_ = features.features; } diff --git a/source/blender/gpu/vulkan/vk_device.hh b/source/blender/gpu/vulkan/vk_device.hh index d51835bea80..7df585b737a 100644 --- a/source/blender/gpu/vulkan/vk_device.hh +++ b/source/blender/gpu/vulkan/vk_device.hh @@ -28,6 +28,18 @@ struct VKWorkarounds { * If set to true we should work around this issue by using a different texture format. */ bool not_aligned_pixel_formats = false; + + /** + * Is the workaround for devices that don't support + * #VkPhysicalDeviceVulkan12Features::shaderOutputViewportIndex enabled. + */ + bool shader_output_viewport_index = false; + + /** + * Is the workaround for devices that don't support + * #VkPhysicalDeviceVulkan12Features::shaderOutputLayer enabled. + */ + bool shader_output_layer = false; }; class VKDevice : public NonCopyable { @@ -63,6 +75,7 @@ class VKDevice : public NonCopyable { /** Features support. */ VkPhysicalDeviceFeatures vk_physical_device_features_ = {}; VkPhysicalDeviceVulkan11Features vk_physical_device_vulkan_11_features_ = {}; + VkPhysicalDeviceVulkan12Features vk_physical_device_vulkan_12_features_ = {}; /** Functions of vk_ext_debugutils for this device/instance. */ debug::VKDebuggingTools debugging_tools_; @@ -101,6 +114,11 @@ class VKDevice : public NonCopyable { return vk_physical_device_vulkan_11_features_; } + const VkPhysicalDeviceVulkan12Features &physical_device_vulkan_12_features_get() const + { + return vk_physical_device_vulkan_12_features_; + } + VkInstance instance_get() const { return vk_instance_; diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 3bc3e4aa6f2..e057d6602a0 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -494,6 +494,8 @@ static char *glsl_patch_get() return patch; } + const VKWorkarounds &workarounds = VKBackend::get().device_get().workarounds_get(); + size_t slen = 0; /* Version need to go first. */ STR_CONCAT(patch, slen, "#version 450\n"); @@ -511,8 +513,12 @@ static char *glsl_patch_get() /* TODO(fclem): This creates a validation error and should be already part of Vulkan 1.2. */ STR_CONCAT(patch, slen, "#extension GL_ARB_shader_viewport_layer_array: enable\n"); - STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n"); - STR_CONCAT(patch, slen, "#define gpu_ViewportIndex gl_ViewportIndex\n"); + if (!workarounds.shader_output_layer) { + STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n"); + } + if (!workarounds.shader_output_viewport_index) { + STR_CONCAT(patch, slen, "#define gpu_ViewportIndex gl_ViewportIndex\n"); + } STR_CONCAT(patch, slen, "#define DFDX_SIGN 1.0\n"); STR_CONCAT(patch, slen, "#define DFDY_SIGN 1.0\n"); @@ -670,6 +676,14 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info) return false; } + if (do_geometry_shader_injection(info)) { + std::string source = workaround_geometry_shader_source_create(*info); + Vector sources; + sources.append("version"); + sources.append(source.c_str()); + geometry_shader_from_glsl(sources); + } + VKShaderInterface *vk_interface = new VKShaderInterface(); vk_interface->init(*info); @@ -1042,6 +1056,7 @@ std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &i { std::stringstream ss; std::string post_main; + const VKWorkarounds &workarounds = VKBackend::get().device_get().workarounds_get(); ss << "\n/* Inputs. */\n"; for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { @@ -1053,6 +1068,13 @@ std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &i for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { print_interface(ss, "out", *iface, location); } + if (workarounds.shader_output_layer && bool(info.builtins_ & BuiltinBits::LAYER)) { + ss << "layout(location=" << (location++) << ") out int gpu_Layer;\n "; + } + if (workarounds.shader_output_viewport_index && + bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX)) { + ss << "layout(location=" << (location++) << ") out int gpu_ViewportIndex;\n"; + } if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { /* Need this for stable barycentric. */ ss << "flat out vec4 gpu_pos_flat;\n"; @@ -1073,6 +1095,7 @@ std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo { std::stringstream ss; std::string pre_main; + const VKWorkarounds &workarounds = VKBackend::get().device_get().workarounds_get(); ss << "\n/* Interfaces. */\n"; const Vector &in_interfaces = info.geometry_source_.is_empty() ? @@ -1082,6 +1105,14 @@ std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo for (const StageInterfaceInfo *iface : in_interfaces) { print_interface(ss, "in", *iface, location); } + if (workarounds.shader_output_layer && bool(info.builtins_ & BuiltinBits::LAYER)) { + ss << "#define gpu_Layer gl_Layer\n"; + } + if (workarounds.shader_output_viewport_index && + bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX)) { + ss << "#define gpu_ViewportIndex gl_ViewportIndex\n"; + } + if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { std::cout << "native" << std::endl; /* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry @@ -1223,6 +1254,102 @@ std::string VKShader::compute_layout_declare(const shader::ShaderCreateInfo &inf return ss.str(); } +/* -------------------------------------------------------------------- */ +/** \name Passthrough geometry shader emulation + * + * \{ */ + +std::string VKShader::workaround_geometry_shader_source_create( + const shader::ShaderCreateInfo &info) +{ + std::stringstream ss; + const VKWorkarounds &workarounds = VKBackend::get().device_get().workarounds_get(); + + const bool do_layer_workaround = workarounds.shader_output_layer && + bool(info.builtins_ & BuiltinBits::LAYER); + const bool do_viewport_workaround = workarounds.shader_output_viewport_index && + bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX); + const bool do_barycentric_workaround = bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD); + + shader::ShaderCreateInfo info_modified = info; + info_modified.geometry_out_interfaces_ = info_modified.vertex_out_interfaces_; + /** + * NOTE(@fclem): Assuming we will render TRIANGLES. This will not work with other primitive + * types. In this case, it might not trigger an error on some implementations. + */ + info_modified.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3); + + ss << geometry_layout_declare(info_modified); + ss << geometry_interface_declare(info_modified); + int location = 0; + for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { + for (const StageInterfaceInfo::InOut &inout : iface->inouts) { + location += get_location_count(inout.type); + } + } + + if (do_layer_workaround) { + ss << "layout(location=" << (location++) << ") in int gpu_Layer[];\n"; + } + if (do_viewport_workaround) { + ss << "layout(location=" << (location++) << ") in int gpu_ViewportIndex[];\n"; + } + if (do_barycentric_workaround) { + ss << "flat out vec4 gpu_pos[3];\n"; + ss << "smooth out vec3 gpu_BaryCoord;\n"; + ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n"; + } + ss << "\n"; + + ss << "void main()\n"; + ss << "{\n"; + if (do_layer_workaround) { + ss << " gl_Layer = gpu_Layer[0];\n"; + } + if (do_viewport_workaround) { + ss << " gl_ViewportIndex = gpu_ViewportIndex[0];\n"; + } + if (do_barycentric_workaround) { + ss << " gpu_pos[0] = gl_in[0].gl_Position;\n"; + ss << " gpu_pos[1] = gl_in[1].gl_Position;\n"; + ss << " gpu_pos[2] = gl_in[2].gl_Position;\n"; + } + for (auto i : IndexRange(3)) { + for (StageInterfaceInfo *iface : info_modified.vertex_out_interfaces_) { + for (auto &inout : iface->inouts) { + ss << " " << iface->instance_name << "_out." << inout.name; + ss << " = " << iface->instance_name << "_in[" << i << "]." << inout.name << ";\n"; + } + } + if (do_barycentric_workaround) { + ss << " gpu_BaryCoordNoPersp = gpu_BaryCoord ="; + ss << " vec3(" << int(i == 0) << ", " << int(i == 1) << ", " << int(i == 2) << ");\n"; + } + ss << " gl_Position = gl_in[" << i << "].gl_Position;\n"; + ss << " EmitVertex();\n"; + } + ss << "}\n"; + return ss.str(); +} + +bool VKShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info) +{ + const VKWorkarounds &workarounds = VKBackend::get().device_get().workarounds_get(); + BuiltinBits builtins = info->builtins_; + if (bool(builtins & BuiltinBits::BARYCENTRIC_COORD)) { + return true; + } + if (workarounds.shader_output_layer && bool(builtins & BuiltinBits::LAYER)) { + return true; + } + if (workarounds.shader_output_viewport_index && bool(builtins & BuiltinBits::VIEWPORT_INDEX)) { + return true; + } + return false; +} + +/** \} */ + int VKShader::program_handle_get() const { return -1; diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index 1070b448a90..656047c7f6f 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -95,6 +95,13 @@ class VKShader : public Shader { { return compute_module_ != VK_NULL_HANDLE; } + + /** + * \brief features available on newer implementation such as native barycentric coordinates + * and layered rendering, necessitate a geometry shader to work on older hardware. + */ + std::string workaround_geometry_shader_source_create(const shader::ShaderCreateInfo &info); + bool do_geometry_shader_injection(const shader::ShaderCreateInfo *info); }; static inline VKShader &unwrap(Shader &shader)