/* * Copyright 2022 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/sksl/ir/SkSLLayout.h" #include "include/private/base/SkAssert.h" #include "src/base/SkMathPriv.h" #include "src/sksl/SkSLContext.h" #include "src/sksl/SkSLErrorReporter.h" #include "src/sksl/SkSLPosition.h" #include "src/sksl/SkSLString.h" namespace SkSL { std::string Layout::paddedDescription() const { std::string result; auto separator = SkSL::String::Separator(); if (fFlags & LayoutFlag::kVulkan) { result += separator() + "vulkan"; } if (fFlags & LayoutFlag::kMetal) { result += separator() + "metal"; } if (fFlags & LayoutFlag::kWebGPU) { result += separator() + "webgpu"; } if (fFlags & LayoutFlag::kDirect3D) { result += separator() + "direct3d"; } if (fFlags & LayoutFlag::kRGBA8) { result += separator() + "rgba8"; } if (fFlags & LayoutFlag::kRGBA32F) { result += separator() + "rgba32f"; } if (fFlags & LayoutFlag::kR32F) { result += separator() + "r32f"; } if (fLocation >= 0) { result += separator() + "location = " + std::to_string(fLocation); } if (fOffset >= 0) { result += separator() + "offset = " + std::to_string(fOffset); } if (fBinding >= 0) { result += separator() + "binding = " + std::to_string(fBinding); } if (fTexture >= 0) { result += separator() + "texture = " + std::to_string(fTexture); } if (fSampler >= 0) { result += separator() + "sampler = " + std::to_string(fSampler); } if (fIndex >= 0) { result += separator() + "index = " + std::to_string(fIndex); } if (fSet >= 0) { result += separator() + "set = " + std::to_string(fSet); } if (fBuiltin >= 0) { result += separator() + "builtin = " + std::to_string(fBuiltin); } if (fInputAttachmentIndex >= 0) { result += separator() + "input_attachment_index = " + std::to_string(fInputAttachmentIndex); } if (fFlags & LayoutFlag::kOriginUpperLeft) { result += separator() + "origin_upper_left"; } if (fFlags & LayoutFlag::kBlendSupportAllEquations) { result += separator() + "blend_support_all_equations"; } if (fFlags & LayoutFlag::kPushConstant) { result += separator() + "push_constant"; } if (fFlags & LayoutFlag::kColor) { result += separator() + "color"; } if (fLocalSizeX >= 0) { result += separator() + "local_size_x = " + std::to_string(fLocalSizeX); } if (fLocalSizeY >= 0) { result += separator() + "local_size_y = " + std::to_string(fLocalSizeY); } if (fLocalSizeZ >= 0) { result += separator() + "local_size_z = " + std::to_string(fLocalSizeZ); } if (result.size() > 0) { result = "layout (" + result + ") "; } return result; } std::string Layout::description() const { std::string s = this->paddedDescription(); if (!s.empty()) { s.pop_back(); } return s; } bool Layout::checkPermittedLayout(const Context& context, Position pos, LayoutFlags permittedLayoutFlags) const { static constexpr struct { LayoutFlag flag; const char* name; } kLayoutFlags[] = { { LayoutFlag::kOriginUpperLeft, "origin_upper_left"}, { LayoutFlag::kPushConstant, "push_constant"}, { LayoutFlag::kBlendSupportAllEquations, "blend_support_all_equations"}, { LayoutFlag::kColor, "color"}, { LayoutFlag::kLocation, "location"}, { LayoutFlag::kOffset, "offset"}, { LayoutFlag::kBinding, "binding"}, { LayoutFlag::kTexture, "texture"}, { LayoutFlag::kSampler, "sampler"}, { LayoutFlag::kIndex, "index"}, { LayoutFlag::kSet, "set"}, { LayoutFlag::kBuiltin, "builtin"}, { LayoutFlag::kInputAttachmentIndex, "input_attachment_index"}, { LayoutFlag::kVulkan, "vulkan"}, { LayoutFlag::kMetal, "metal"}, { LayoutFlag::kWebGPU, "webgpu"}, { LayoutFlag::kDirect3D, "direct3d"}, { LayoutFlag::kRGBA8, "rgba8"}, { LayoutFlag::kRGBA32F, "rgba32f"}, { LayoutFlag::kR32F, "r32f"}, { LayoutFlag::kLocalSizeX, "local_size_x"}, { LayoutFlag::kLocalSizeY, "local_size_y"}, { LayoutFlag::kLocalSizeZ, "local_size_z"}, }; bool success = true; LayoutFlags layoutFlags = fFlags; LayoutFlags backendFlags = layoutFlags & LayoutFlag::kAllBackends; if (SkPopCount(backendFlags.value()) > 1) { context.fErrors->error(pos, "only one backend qualifier can be used"); success = false; } LayoutFlags pixelFormatFlags = layoutFlags & LayoutFlag::kAllPixelFormats; if (SkPopCount(pixelFormatFlags.value()) > 1) { context.fErrors->error(pos, "only one pixel format qualifier can be used"); success = false; } if ((layoutFlags & (LayoutFlag::kTexture | LayoutFlag::kSampler)) && layoutFlags & LayoutFlag::kBinding) { context.fErrors->error(pos, "'binding' modifier cannot coexist with 'texture'/'sampler'"); success = false; } // The `texture` and `sampler` flags are only allowed when targeting Metal, WebGPU or Direct3D. if (!(layoutFlags & (LayoutFlag::kMetal | LayoutFlag::kWebGPU | LayoutFlag::kDirect3D))) { permittedLayoutFlags &= ~LayoutFlag::kTexture; permittedLayoutFlags &= ~LayoutFlag::kSampler; } // The `push_constant` flag is only allowed when targeting Vulkan or WebGPU. if (!(layoutFlags & (LayoutFlag::kVulkan | LayoutFlag::kWebGPU))) { permittedLayoutFlags &= ~LayoutFlag::kPushConstant; } // The `set` flag is not allowed when explicitly targeting Metal. if (layoutFlags & LayoutFlag::kMetal) { permittedLayoutFlags &= ~LayoutFlag::kSet; } for (const auto& lf : kLayoutFlags) { if (layoutFlags & lf.flag) { if (!(permittedLayoutFlags & lf.flag)) { context.fErrors->error(pos, "layout qualifier '" + std::string(lf.name) + "' is not permitted here"); success = false; } layoutFlags &= ~lf.flag; } } SkASSERT(layoutFlags == LayoutFlag::kNone); return success; } bool Layout::operator==(const Layout& other) const { return fFlags == other.fFlags && fLocation == other.fLocation && fOffset == other.fOffset && fBinding == other.fBinding && fTexture == other.fTexture && fSampler == other.fSampler && fIndex == other.fIndex && fSet == other.fSet && fBuiltin == other.fBuiltin && fInputAttachmentIndex == other.fInputAttachmentIndex && fLocalSizeX == other.fLocalSizeX && fLocalSizeY == other.fLocalSizeY && fLocalSizeZ == other.fLocalSizeZ; } } // namespace SkSL