/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2022 Blender Foundation. All rights reserved. */ /** \file * \ingroup gpu */ #include "vk_shader.hh" #include "vk_backend.hh" #include "vk_shader_log.hh" #include "BLI_string_utils.h" #include "BLI_vector.hh" using namespace blender::gpu::shader; extern "C" char datatoc_glsl_shader_defines_glsl[]; namespace blender::gpu { /* -------------------------------------------------------------------- */ /** \name Create Info * \{ */ static const char *to_string(const Interpolation &interp) { switch (interp) { case Interpolation::SMOOTH: return "smooth"; case Interpolation::FLAT: return "flat"; case Interpolation::NO_PERSPECTIVE: return "noperspective"; default: return "unknown"; } } static const char *to_string(const Type &type) { switch (type) { case Type::FLOAT: return "float"; case Type::VEC2: return "vec2"; case Type::VEC3: return "vec3"; case Type::VEC4: return "vec4"; case Type::MAT3: return "mat3"; case Type::MAT4: return "mat4"; case Type::UINT: return "uint"; case Type::UVEC2: return "uvec2"; case Type::UVEC3: return "uvec3"; case Type::UVEC4: return "uvec4"; case Type::INT: return "int"; case Type::IVEC2: return "ivec2"; case Type::IVEC3: return "ivec3"; case Type::IVEC4: return "ivec4"; case Type::BOOL: return "bool"; default: return "unknown"; } } static const char *to_string(const eGPUTextureFormat &type) { switch (type) { case GPU_RGBA8UI: return "rgba8ui"; case GPU_RGBA8I: return "rgba8i"; case GPU_RGBA8: return "rgba8"; case GPU_RGBA32UI: return "rgba32ui"; case GPU_RGBA32I: return "rgba32i"; case GPU_RGBA32F: return "rgba32f"; case GPU_RGBA16UI: return "rgba16ui"; case GPU_RGBA16I: return "rgba16i"; case GPU_RGBA16F: return "rgba16f"; case GPU_RGBA16: return "rgba16"; case GPU_RG8UI: return "rg8ui"; case GPU_RG8I: return "rg8i"; case GPU_RG8: return "rg8"; case GPU_RG32UI: return "rg32ui"; case GPU_RG32I: return "rg32i"; case GPU_RG32F: return "rg32f"; case GPU_RG16UI: return "rg16ui"; case GPU_RG16I: return "rg16i"; case GPU_RG16F: return "rg16f"; case GPU_RG16: return "rg16"; case GPU_R8UI: return "r8ui"; case GPU_R8I: return "r8i"; case GPU_R8: return "r8"; case GPU_R32UI: return "r32ui"; case GPU_R32I: return "r32i"; case GPU_R32F: return "r32f"; case GPU_R16UI: return "r16ui"; case GPU_R16I: return "r16i"; case GPU_R16F: return "r16f"; case GPU_R16: return "r16"; case GPU_R11F_G11F_B10F: return "r11f_g11f_b10f"; case GPU_RGB10_A2: return "rgb10_a2"; default: return "unknown"; } } static const char *to_string(const PrimitiveIn &layout) { switch (layout) { case PrimitiveIn::POINTS: return "points"; case PrimitiveIn::LINES: return "lines"; case PrimitiveIn::LINES_ADJACENCY: return "lines_adjacency"; case PrimitiveIn::TRIANGLES: return "triangles"; case PrimitiveIn::TRIANGLES_ADJACENCY: return "triangles_adjacency"; default: return "unknown"; } } static const char *to_string(const PrimitiveOut &layout) { switch (layout) { case PrimitiveOut::POINTS: return "points"; case PrimitiveOut::LINE_STRIP: return "line_strip"; case PrimitiveOut::TRIANGLE_STRIP: return "triangle_strip"; default: return "unknown"; } } static const char *to_string(const DepthWrite &value) { switch (value) { case DepthWrite::ANY: return "depth_any"; case DepthWrite::GREATER: return "depth_greater"; case DepthWrite::LESS: return "depth_less"; default: return "depth_unchanged"; } } static void print_image_type(std::ostream &os, const ImageType &type, const ShaderCreateInfo::Resource::BindType bind_type) { switch (type) { case ImageType::INT_BUFFER: case ImageType::INT_1D: case ImageType::INT_1D_ARRAY: case ImageType::INT_2D: case ImageType::INT_2D_ARRAY: case ImageType::INT_3D: case ImageType::INT_CUBE: case ImageType::INT_CUBE_ARRAY: os << "i"; break; case ImageType::UINT_BUFFER: case ImageType::UINT_1D: case ImageType::UINT_1D_ARRAY: case ImageType::UINT_2D: case ImageType::UINT_2D_ARRAY: case ImageType::UINT_3D: case ImageType::UINT_CUBE: case ImageType::UINT_CUBE_ARRAY: os << "u"; break; default: break; } if (bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) { os << "image"; } else { os << "sampler"; } switch (type) { case ImageType::FLOAT_BUFFER: case ImageType::INT_BUFFER: case ImageType::UINT_BUFFER: os << "Buffer"; break; case ImageType::FLOAT_1D: case ImageType::FLOAT_1D_ARRAY: case ImageType::INT_1D: case ImageType::INT_1D_ARRAY: case ImageType::UINT_1D: case ImageType::UINT_1D_ARRAY: os << "1D"; break; case ImageType::FLOAT_2D: case ImageType::FLOAT_2D_ARRAY: case ImageType::INT_2D: case ImageType::INT_2D_ARRAY: case ImageType::UINT_2D: case ImageType::UINT_2D_ARRAY: case ImageType::SHADOW_2D: case ImageType::SHADOW_2D_ARRAY: case ImageType::DEPTH_2D: case ImageType::DEPTH_2D_ARRAY: os << "2D"; break; case ImageType::FLOAT_3D: case ImageType::INT_3D: case ImageType::UINT_3D: os << "3D"; break; case ImageType::FLOAT_CUBE: case ImageType::FLOAT_CUBE_ARRAY: case ImageType::INT_CUBE: case ImageType::INT_CUBE_ARRAY: case ImageType::UINT_CUBE: case ImageType::UINT_CUBE_ARRAY: case ImageType::SHADOW_CUBE: case ImageType::SHADOW_CUBE_ARRAY: case ImageType::DEPTH_CUBE: case ImageType::DEPTH_CUBE_ARRAY: os << "Cube"; break; default: break; } switch (type) { case ImageType::FLOAT_1D_ARRAY: case ImageType::FLOAT_2D_ARRAY: case ImageType::FLOAT_CUBE_ARRAY: case ImageType::INT_1D_ARRAY: case ImageType::INT_2D_ARRAY: case ImageType::INT_CUBE_ARRAY: case ImageType::UINT_1D_ARRAY: case ImageType::UINT_2D_ARRAY: case ImageType::UINT_CUBE_ARRAY: case ImageType::SHADOW_2D_ARRAY: case ImageType::SHADOW_CUBE_ARRAY: case ImageType::DEPTH_2D_ARRAY: case ImageType::DEPTH_CUBE_ARRAY: os << "Array"; break; default: break; } switch (type) { case ImageType::SHADOW_2D: case ImageType::SHADOW_2D_ARRAY: case ImageType::SHADOW_CUBE: case ImageType::SHADOW_CUBE_ARRAY: os << "Shadow"; break; default: break; } os << " "; } static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifiers) { if (bool(qualifiers & Qualifier::NO_RESTRICT) == false) { os << "restrict "; } if (bool(qualifiers & Qualifier::READ) == false) { os << "writeonly "; } if (bool(qualifiers & Qualifier::WRITE) == false) { os << "readonly "; } return os; } static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res) { os << "layout(binding = " << res.slot; if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) { os << ", " << to_string(res.image.format); } else if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) { os << ", std140"; } else if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) { os << ", std430"; } os << ") "; int64_t array_offset; StringRef name_no_array; switch (res.bind_type) { case ShaderCreateInfo::Resource::BindType::SAMPLER: os << "uniform "; print_image_type(os, res.sampler.type, res.bind_type); os << res.sampler.name << ";\n"; break; case ShaderCreateInfo::Resource::BindType::IMAGE: os << "uniform "; print_qualifier(os, res.image.qualifiers); print_image_type(os, res.image.type, res.bind_type); os << res.image.name << ";\n"; break; case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: array_offset = res.uniformbuf.name.find_first_of("["); name_no_array = (array_offset == -1) ? res.uniformbuf.name : StringRef(res.uniformbuf.name.c_str(), array_offset); os << "uniform " << name_no_array << " { " << res.uniformbuf.type_name << " _" << res.uniformbuf.name << "; };\n"; break; case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER: array_offset = res.storagebuf.name.find_first_of("["); name_no_array = (array_offset == -1) ? res.storagebuf.name : StringRef(res.storagebuf.name.c_str(), array_offset); print_qualifier(os, res.storagebuf.qualifiers); os << "buffer "; os << name_no_array << " { " << res.storagebuf.type_name << " _" << res.storagebuf.name << "; };\n"; break; } } static void print_resource_alias(std::ostream &os, const ShaderCreateInfo::Resource &res) { int64_t array_offset; StringRef name_no_array; switch (res.bind_type) { case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: array_offset = res.uniformbuf.name.find_first_of("["); name_no_array = (array_offset == -1) ? res.uniformbuf.name : StringRef(res.uniformbuf.name.c_str(), array_offset); os << "#define " << name_no_array << " (_" << name_no_array << ")\n"; break; case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER: array_offset = res.storagebuf.name.find_first_of("["); name_no_array = (array_offset == -1) ? res.storagebuf.name : StringRef(res.storagebuf.name.c_str(), array_offset); os << "#define " << name_no_array << " (_" << name_no_array << ")\n"; break; default: break; } } inline int get_location_count(const Type &type) { if (type == shader::Type::MAT4) { return 4; } else if (type == shader::Type::MAT3) { return 3; } return 1; } static void print_interface(std::ostream &os, const std::string &prefix, const StageInterfaceInfo &iface, int &location, const StringRefNull &suffix = "") { if (iface.instance_name.is_empty()) { for (const StageInterfaceInfo::InOut &inout : iface.inouts) { os << "layout(location=" << location << ") " << prefix << " " << to_string(inout.interp) << " " << to_string(inout.type) << " " << inout.name << ";\n"; location += get_location_count(inout.type); } } else { std::string struct_name = prefix + iface.name; std::string iface_attribute; if (iface.instance_name.is_empty()) { iface_attribute = "iface_"; } else { iface_attribute = iface.instance_name; } std::string flat = ""; if (prefix == "in") { flat = "flat "; } const bool add_defines = iface.instance_name.is_empty(); os << "struct " << struct_name << " {\n"; for (const StageInterfaceInfo::InOut &inout : iface.inouts) { os << " " << to_string(inout.type) << " " << inout.name << ";\n"; } os << "};\n"; os << "layout(location=" << location << ") " << prefix << " " << flat << struct_name << " " << iface_attribute << suffix << ";\n"; if (add_defines) { for (const StageInterfaceInfo::InOut &inout : iface.inouts) { os << "#define " << inout.name << " (" << iface_attribute << "." << inout.name << ")\n"; } } for (const StageInterfaceInfo::InOut &inout : iface.inouts) { location += get_location_count(inout.type); } } } /** \} */ static std::string main_function_wrapper(std::string &pre_main, std::string &post_main) { std::stringstream ss; /* Prototype for the original main. */ ss << "\n"; ss << "void main_function_();\n"; /* Wrapper to the main function in order to inject code processing on globals. */ ss << "void main() {\n"; ss << pre_main; ss << " main_function_();\n"; ss << post_main; ss << "}\n"; /* Rename the original main. */ ss << "#define main main_function_\n"; ss << "\n"; return ss.str(); } static const std::string to_stage_name(shaderc_shader_kind stage) { switch (stage) { case shaderc_vertex_shader: return std::string("vertex"); case shaderc_geometry_shader: return std::string("geometry"); case shaderc_fragment_shader: return std::string("fragment"); case shaderc_compute_shader: return std::string("compute"); default: BLI_assert_msg(false, "Do not know how to convert shaderc_shader_kind to stage name."); break; } return std::string("unknown stage"); } static char *glsl_patch_get() { static char patch[2048] = "\0"; if (patch[0] != '\0') { return patch; } size_t slen = 0; /* Version need to go first. */ STR_CONCAT(patch, slen, "#version 450\n"); STR_CONCAT(patch, slen, "#define gl_VertexID gl_VertexIndex\n"); STR_CONCAT(patch, slen, "#define gpu_BaseInstance (0)\n"); STR_CONCAT(patch, slen, "#define gpu_InstanceIndex (gl_InstanceIndex)\n"); STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); STR_CONCAT(patch, slen, "#define gl_InstanceID gpu_InstanceIndex\n"); STR_CONCAT(patch, slen, "#define DFDX_SIGN 1.0\n"); STR_CONCAT(patch, slen, "#define DFDY_SIGN 1.0\n"); /* GLSL Backend Lib. */ STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl); BLI_assert(slen < sizeof(patch)); return patch; } static std::string combine_sources(Span sources) { char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size()); std::string result(sources_combined); MEM_freeN(sources_combined); return result; } Vector VKShader::compile_glsl_to_spirv(Span sources, shaderc_shader_kind stage) { std::string combined_sources = combine_sources(sources); VKBackend &backend = static_cast(*VKBackend::get()); shaderc::Compiler &compiler = backend.get_shaderc_compiler(); shaderc::CompileOptions options; options.SetOptimizationLevel(shaderc_optimization_level_performance); shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv( combined_sources, stage, name, options); if (module.GetNumErrors() != 0 || module.GetNumWarnings() != 0) { std::string log = module.GetErrorMessage(); Vector logcstr(log.c_str(), log.c_str() + log.size() + 1); VKLogParser parser; print_log(sources, logcstr.data(), to_stage_name(stage).c_str(), module.GetCompilationStatus() != shaderc_compilation_status_success, &parser); } if (module.GetCompilationStatus() != shaderc_compilation_status_success) { compilation_failed_ = true; return Vector(); } return Vector(module.cbegin(), module.cend()); } void VKShader::build_shader_module(Span spirv_module, VkShaderModule *r_shader_module) { VkShaderModuleCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.codeSize = spirv_module.size() * sizeof(uint32_t); create_info.pCode = spirv_module.data(); VKContext &context = *static_cast(VKContext::get()); VkResult result = vkCreateShaderModule( context.device_get(), &create_info, nullptr, r_shader_module); if (result != VK_SUCCESS) { compilation_failed_ = true; *r_shader_module = VK_NULL_HANDLE; } } VKShader::VKShader(const char *name) : Shader(name) { context_ = VKContext::get(); } VKShader::~VKShader() { VkDevice device = context_->device_get(); if (vertex_module_ != VK_NULL_HANDLE) { vkDestroyShaderModule(device, vertex_module_, nullptr); vertex_module_ = VK_NULL_HANDLE; } if (geometry_module_ != VK_NULL_HANDLE) { vkDestroyShaderModule(device, geometry_module_, nullptr); geometry_module_ = VK_NULL_HANDLE; } if (fragment_module_ != VK_NULL_HANDLE) { vkDestroyShaderModule(device, fragment_module_, nullptr); fragment_module_ = VK_NULL_HANDLE; } if (compute_module_ != VK_NULL_HANDLE) { vkDestroyShaderModule(device, compute_module_, nullptr); compute_module_ = VK_NULL_HANDLE; } } void VKShader::build_shader_module(MutableSpan sources, shaderc_shader_kind stage, VkShaderModule *r_shader_module) { BLI_assert_msg(ELEM(stage, shaderc_vertex_shader, shaderc_geometry_shader, shaderc_fragment_shader, shaderc_compute_shader), "Only forced ShaderC shader kinds are supported."); sources[0] = glsl_patch_get(); Vector spirv_module = compile_glsl_to_spirv(sources, stage); build_shader_module(spirv_module, r_shader_module); } void VKShader::vertex_shader_from_glsl(MutableSpan sources) { build_shader_module(sources, shaderc_vertex_shader, &vertex_module_); } void VKShader::geometry_shader_from_glsl(MutableSpan sources) { build_shader_module(sources, shaderc_geometry_shader, &geometry_module_); } void VKShader::fragment_shader_from_glsl(MutableSpan sources) { build_shader_module(sources, shaderc_fragment_shader, &fragment_module_); } void VKShader::compute_shader_from_glsl(MutableSpan sources) { build_shader_module(sources, shaderc_compute_shader, &compute_module_); } bool VKShader::finalize(const shader::ShaderCreateInfo *info) { if (compilation_failed_) { return false; } if (vertex_module_ != VK_NULL_HANDLE) { BLI_assert((fragment_module_ != VK_NULL_HANDLE && info->tf_type_ == GPU_SHADER_TFB_NONE) || (fragment_module_ == VK_NULL_HANDLE && info->tf_type_ != GPU_SHADER_TFB_NONE)); BLI_assert(compute_module_ == VK_NULL_HANDLE); VkPipelineShaderStageCreateInfo vertex_stage_info = {}; vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; vertex_stage_info.module = vertex_module_; vertex_stage_info.pName = "main"; pipeline_infos_.append(vertex_stage_info); if (geometry_module_ != VK_NULL_HANDLE) { VkPipelineShaderStageCreateInfo geo_stage_info = {}; geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; geo_stage_info.module = geometry_module_; geo_stage_info.pName = "main"; pipeline_infos_.append(geo_stage_info); } if (fragment_module_ != VK_NULL_HANDLE) { VkPipelineShaderStageCreateInfo fragment_stage_info = {}; fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fragment_stage_info.module = fragment_module_; fragment_stage_info.pName = "main"; pipeline_infos_.append(fragment_stage_info); } } else { BLI_assert(vertex_module_ == VK_NULL_HANDLE); BLI_assert(geometry_module_ == VK_NULL_HANDLE); BLI_assert(fragment_module_ == VK_NULL_HANDLE); BLI_assert(compute_module_ != VK_NULL_HANDLE); VkPipelineShaderStageCreateInfo compute_stage_info = {}; compute_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; compute_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; compute_stage_info.module = compute_module_; compute_stage_info.pName = "main"; pipeline_infos_.append(compute_stage_info); } #ifdef NDEBUG UNUSED_VARS(info); #endif return true; } void VKShader::transform_feedback_names_set(Span /*name_list*/, eGPUShaderTFBType /*geom_type*/) { } bool VKShader::transform_feedback_enable(GPUVertBuf *) { return false; } void VKShader::transform_feedback_disable() { } void VKShader::bind() { } void VKShader::unbind() { } void VKShader::uniform_float(int /*location*/, int /*comp_len*/, int /*array_size*/, const float * /*data*/) { } void VKShader::uniform_int(int /*location*/, int /*comp_len*/, int /*array_size*/, const int * /*data*/) { } std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const { std::stringstream ss; ss << "\n/* Pass Resources. */\n"; for (const ShaderCreateInfo::Resource &res : info.pass_resources_) { print_resource(ss, res); } for (const ShaderCreateInfo::Resource &res : info.pass_resources_) { print_resource_alias(ss, res); } ss << "\n/* Batch Resources. */\n"; for (const ShaderCreateInfo::Resource &res : info.batch_resources_) { print_resource(ss, res); } for (const ShaderCreateInfo::Resource &res : info.batch_resources_) { print_resource_alias(ss, res); } if (!info.push_constants_.is_empty()) { ss << "\n/* Push Constants. */\n"; ss << "layout(push_constant) uniform constants\n"; ss << "{\n"; for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) { ss << " " << to_string(uniform.type) << " pc_" << uniform.name; if (uniform.array_size > 0) { ss << "[" << uniform.array_size << "]"; } ss << ";\n"; } ss << "} PushConstants;\n"; for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) { ss << "#define " << uniform.name << " (PushConstants.pc_" << uniform.name << ")\n"; } } ss << "\n"; return ss.str(); } std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &info) const { std::stringstream ss; std::string post_main; ss << "\n/* Inputs. */\n"; for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { ss << "layout(location = " << attr.index << ") "; ss << "in " << to_string(attr.type) << " " << attr.name << ";\n"; } /* NOTE(D4490): Fix a bug where shader without any vertex attributes do not behave correctly. */ if (GPU_type_matches_ex(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) && info.vertex_inputs_.is_empty()) { ss << "in float gpu_dummy_workaround;\n"; } ss << "\n/* Interfaces. */\n"; int location = 0; for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { print_interface(ss, "out", *iface, location); } if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { /* Need this for stable barycentric. */ ss << "flat out vec4 gpu_pos_flat;\n"; ss << "out vec4 gpu_pos;\n"; post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n"; } ss << "\n"; if (post_main.empty() == false) { std::string pre_main; ss << main_function_wrapper(pre_main, post_main); } return ss.str(); } std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo &info) const { std::stringstream ss; std::string pre_main; ss << "\n/* Interfaces. */\n"; const Vector &in_interfaces = info.geometry_source_.is_empty() ? info.vertex_out_interfaces_ : info.geometry_out_interfaces_; int location = 0; for (const StageInterfaceInfo *iface : in_interfaces) { print_interface(ss, "in", *iface, location); } 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 * shader workaround if this extension/feature is detected. */ ss << "\n/* Stable Barycentric Coordinates. */\n"; ss << "flat in vec4 gpu_pos_flat;\n"; ss << "__explicitInterpAMD in vec4 gpu_pos;\n"; /* Globals. */ ss << "vec3 gpu_BaryCoord;\n"; ss << "vec3 gpu_BaryCoordNoPersp;\n"; ss << "\n"; ss << "vec2 stable_bary_(vec2 in_bary) {\n"; ss << " vec3 bary = vec3(in_bary, 1.0 - in_bary.x - in_bary.y);\n"; ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { return bary.zxy; }\n"; ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n"; ss << " return bary.xyz;\n"; ss << "}\n"; ss << "\n"; ss << "vec4 gpu_position_at_vertex(int v) {\n"; ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { v = (v + 2) % 3; }\n"; ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { v = (v + 1) % 3; }\n"; ss << " return interpolateAtVertexAMD(gpu_pos, v);\n"; ss << "}\n"; pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n"; pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n"; } if (info.early_fragment_test_) { ss << "layout(early_fragment_tests) in;\n"; } ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n"; ss << "\n/* Outputs. */\n"; for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) { ss << "layout(location = " << output.index; switch (output.blend) { case DualBlend::SRC_0: ss << ", index = 0"; break; case DualBlend::SRC_1: ss << ", index = 1"; break; default: break; } ss << ") "; ss << "out " << to_string(output.type) << " " << output.name << ";\n"; } ss << "\n"; if (pre_main.empty() == false) { std::string post_main; ss << main_function_wrapper(pre_main, post_main); } return ss.str(); } std::string VKShader::geometry_interface_declare(const shader::ShaderCreateInfo &info) const { int max_verts = info.geometry_layout_.max_vertices; int invocations = info.geometry_layout_.invocations; std::stringstream ss; ss << "\n/* Geometry Layout. */\n"; ss << "layout(" << to_string(info.geometry_layout_.primitive_in); if (invocations != -1) { ss << ", invocations = " << invocations; } ss << ") in;\n"; ss << "layout(" << to_string(info.geometry_layout_.primitive_out) << ", max_vertices = " << max_verts << ") out;\n"; ss << "\n"; return ss.str(); } static StageInterfaceInfo *find_interface_by_name(const Vector &ifaces, const StringRefNull &name) { for (auto *iface : ifaces) { if (iface->instance_name == name) { return iface; } } return nullptr; } std::string VKShader::geometry_layout_declare(const shader::ShaderCreateInfo &info) const { std::stringstream ss; ss << "\n/* Interfaces. */\n"; int location = 0; for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { bool has_matching_output_iface = find_interface_by_name(info.geometry_out_interfaces_, iface->instance_name) != nullptr; const char *suffix = (has_matching_output_iface) ? "_in[]" : "[]"; print_interface(ss, "in", *iface, location, suffix); } ss << "\n"; for (const StageInterfaceInfo *iface : info.geometry_out_interfaces_) { bool has_matching_input_iface = find_interface_by_name(info.vertex_out_interfaces_, iface->instance_name) != nullptr; const char *suffix = (has_matching_input_iface) ? "_out" : ""; print_interface(ss, "out", *iface, location, suffix); } ss << "\n"; return ss.str(); } std::string VKShader::compute_layout_declare(const shader::ShaderCreateInfo &info) const { std::stringstream ss; ss << "\n/* Compute Layout. */\n"; ss << "layout(local_size_x = " << info.compute_layout_.local_size_x; if (info.compute_layout_.local_size_y != -1) { ss << ", local_size_y = " << info.compute_layout_.local_size_y; } if (info.compute_layout_.local_size_z != -1) { ss << ", local_size_z = " << info.compute_layout_.local_size_z; } ss << ") in;\n"; ss << "\n"; return ss.str(); } int VKShader::program_handle_get() const { return -1; } } // namespace blender::gpu