2 * Copyright (C) 2019 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "WHLSLSemanticMatcher.h"
31 #include "WHLSLBuiltInSemantic.h"
32 #include "WHLSLFunctionDefinition.h"
33 #include "WHLSLGatherEntryPointItems.h"
34 #include "WHLSLInferTypes.h"
35 #include "WHLSLPipelineDescriptor.h"
36 #include "WHLSLProgram.h"
37 #include "WHLSLResourceSemantic.h"
38 #include "WHLSLStageInOutSemantic.h"
39 #include <wtf/HashMap.h>
40 #include <wtf/HashSet.h>
41 #include <wtf/Optional.h>
42 #include <wtf/text/WTFString.h>
48 static bool matchMode(Binding::BindingDetails bindingType, AST::ResourceSemantic::Mode mode)
50 return WTF::visit(WTF::makeVisitor([&](UniformBufferBinding) -> bool {
51 return mode == AST::ResourceSemantic::Mode::Buffer;
52 }, [&](SamplerBinding) -> bool {
53 return mode == AST::ResourceSemantic::Mode::Sampler;
54 }, [&](TextureBinding) -> bool {
55 return mode == AST::ResourceSemantic::Mode::Texture;
56 }, [&](StorageBufferBinding) -> bool {
57 return mode == AST::ResourceSemantic::Mode::UnorderedAccessView;
61 static Optional<HashMap<Binding*, size_t>> matchResources(Vector<EntryPointItem>& entryPointItems, Layout& layout, ShaderStage shaderStage)
63 HashMap<Binding*, size_t> result;
64 HashSet<size_t, DefaultHash<size_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<size_t>> itemIndices;
65 for (auto& bindGroup : layout) {
66 auto space = bindGroup.name;
67 for (auto& binding : bindGroup.bindings) {
68 if (!binding.visibility.contains(shaderStage))
70 for (size_t i = 0; i < entryPointItems.size(); ++i) {
71 auto& item = entryPointItems[i];
72 auto& semantic = *item.semantic;
73 if (!WTF::holds_alternative<AST::ResourceSemantic>(semantic))
75 auto& resourceSemantic = WTF::get<AST::ResourceSemantic>(semantic);
76 if (!matchMode(binding.binding, resourceSemantic.mode()))
78 if (binding.externalName != resourceSemantic.index())
80 if (space != resourceSemantic.space())
82 result.add(&binding, i);
88 for (size_t i = 0; i < entryPointItems.size(); ++i) {
89 auto& item = entryPointItems[i];
90 auto& semantic = *item.semantic;
91 if (!WTF::holds_alternative<AST::ResourceSemantic>(semantic))
93 if (!itemIndices.contains(i))
100 static bool matchInputsOutputs(Vector<EntryPointItem>& vertexOutputs, Vector<EntryPointItem>& fragmentInputs)
102 for (auto& fragmentInput : fragmentInputs) {
103 if (!WTF::holds_alternative<AST::StageInOutSemantic>(*fragmentInput.semantic))
105 auto& fragmentInputStageInOutSemantic = WTF::get<AST::StageInOutSemantic>(*fragmentInput.semantic);
107 for (auto& vertexOutput : vertexOutputs) {
108 if (!WTF::holds_alternative<AST::StageInOutSemantic>(*vertexOutput.semantic))
110 auto& vertexOutputStageInOutSemantic = WTF::get<AST::StageInOutSemantic>(*vertexOutput.semantic);
111 if (fragmentInputStageInOutSemantic.index() == vertexOutputStageInOutSemantic.index()) {
112 if (matches(*fragmentInput.unnamedType, *vertexOutput.unnamedType)) {
125 static bool isAcceptableFormat(VertexFormat vertexFormat, AST::UnnamedType& unnamedType, Intrinsics& intrinsics)
127 switch (vertexFormat) {
128 case VertexFormat::FloatR32G32B32A32:
129 return matches(unnamedType, intrinsics.float4Type());
130 case VertexFormat::FloatR32G32B32:
131 return matches(unnamedType, intrinsics.float3Type());
132 case VertexFormat::FloatR32G32:
133 return matches(unnamedType, intrinsics.float2Type());
135 ASSERT(vertexFormat == VertexFormat::FloatR32);
136 return matches(unnamedType, intrinsics.floatType());
140 static Optional<HashMap<VertexAttribute*, size_t>> matchVertexAttributes(Vector<EntryPointItem>& vertexInputs, VertexAttributes& vertexAttributes, Intrinsics& intrinsics)
142 HashMap<VertexAttribute*, size_t> result;
143 HashSet<size_t, DefaultHash<size_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<size_t>> itemIndices;
144 for (auto& vertexAttribute : vertexAttributes) {
145 for (size_t i = 0; i < vertexInputs.size(); ++i) {
146 auto& item = vertexInputs[i];
147 auto& semantic = *item.semantic;
148 if (!WTF::holds_alternative<AST::StageInOutSemantic>(semantic))
150 auto& stageInOutSemantic = WTF::get<AST::StageInOutSemantic>(semantic);
151 if (stageInOutSemantic.index() != vertexAttribute.shaderLocation)
153 if (!isAcceptableFormat(vertexAttribute.vertexFormat, *item.unnamedType, intrinsics))
155 result.add(&vertexAttribute, i);
160 for (size_t i = 0; i < vertexInputs.size(); ++i) {
161 auto& item = vertexInputs[i];
162 auto& semantic = *item.semantic;
163 if (!WTF::holds_alternative<AST::StageInOutSemantic>(semantic))
165 if (!itemIndices.contains(i))
172 static bool isAcceptableFormat(TextureFormat textureFormat, AST::UnnamedType& unnamedType, Intrinsics& intrinsics, bool isColor)
175 switch (textureFormat) {
176 case TextureFormat::R8Unorm:
177 case TextureFormat::R8UnormSrgb:
178 case TextureFormat::R8Snorm:
179 case TextureFormat::R16Unorm:
180 case TextureFormat::R16Snorm:
181 case TextureFormat::R16Float:
182 case TextureFormat::R32Float:
183 return matches(unnamedType, intrinsics.floatType());
184 case TextureFormat::RG8Unorm:
185 case TextureFormat::RG8UnormSrgb:
186 case TextureFormat::RG8Snorm:
187 case TextureFormat::RG16Unorm:
188 case TextureFormat::RG16Snorm:
189 case TextureFormat::RG16Float:
190 case TextureFormat::RG32Float:
191 return matches(unnamedType, intrinsics.float2Type());
192 case TextureFormat::B5G6R5Unorm:
193 case TextureFormat::RG11B10Float:
194 return matches(unnamedType, intrinsics.float3Type());
195 case TextureFormat::RGBA8Unorm:
196 case TextureFormat::RGBA8UnormSrgb:
197 case TextureFormat::BGRA8Unorm:
198 case TextureFormat::BGRA8UnormSrgb:
199 case TextureFormat::RGBA8Snorm:
200 case TextureFormat::RGB10A2Unorm:
201 case TextureFormat::RGBA16Unorm:
202 case TextureFormat::RGBA16Snorm:
203 case TextureFormat::RGBA16Float:
204 case TextureFormat::RGBA32Float:
205 return matches(unnamedType, intrinsics.float4Type());
206 case TextureFormat::R32Uint:
207 return matches(unnamedType, intrinsics.uintType());
208 case TextureFormat::R32Sint:
209 return matches(unnamedType, intrinsics.intType());
210 case TextureFormat::RG32Uint:
211 return matches(unnamedType, intrinsics.uint2Type());
212 case TextureFormat::RG32Sint:
213 return matches(unnamedType, intrinsics.int2Type());
214 case TextureFormat::RGBA32Uint:
215 return matches(unnamedType, intrinsics.uint4Type());
216 case TextureFormat::RGBA32Sint:
217 return matches(unnamedType, intrinsics.int4Type());
219 ASSERT_NOT_REACHED();
226 static Optional<HashMap<AttachmentDescriptor*, size_t>> matchColorAttachments(Vector<EntryPointItem>& fragmentOutputs, Vector<AttachmentDescriptor>& attachmentDescriptors, Intrinsics& intrinsics)
228 HashMap<AttachmentDescriptor*, size_t> result;
229 HashSet<size_t, DefaultHash<size_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<size_t>> itemIndices;
230 for (auto& attachmentDescriptor : attachmentDescriptors) {
231 for (size_t i = 0; i < fragmentOutputs.size(); ++i) {
232 auto& item = fragmentOutputs[i];
233 auto& semantic = *item.semantic;
234 if (!WTF::holds_alternative<AST::StageInOutSemantic>(semantic))
236 auto& stageInOutSemantic = WTF::get<AST::StageInOutSemantic>(semantic);
237 if (stageInOutSemantic.index() != attachmentDescriptor.name)
239 if (!isAcceptableFormat(attachmentDescriptor.textureFormat, *item.unnamedType, intrinsics, true))
241 result.add(&attachmentDescriptor, i);
246 for (size_t i = 0; i < fragmentOutputs.size(); ++i) {
247 auto& item = fragmentOutputs[i];
248 auto& semantic = *item.semantic;
249 if (!WTF::holds_alternative<AST::StageInOutSemantic>(semantic))
251 if (!itemIndices.contains(i))
258 static bool matchDepthAttachment(Vector<EntryPointItem>& fragmentOutputs, Optional<AttachmentDescriptor>& depthStencilAttachmentDescriptor, Intrinsics& intrinsics)
260 auto iterator = std::find_if(fragmentOutputs.begin(), fragmentOutputs.end(), [&](EntryPointItem& item) {
261 auto& semantic = *item.semantic;
262 if (!WTF::holds_alternative<AST::BuiltInSemantic>(semantic))
264 auto& builtInSemantic = WTF::get<AST::BuiltInSemantic>(semantic);
265 return builtInSemantic.variable() == AST::BuiltInSemantic::Variable::SVDepth;
267 if (iterator == fragmentOutputs.end())
270 if (depthStencilAttachmentDescriptor) {
271 ASSERT(!depthStencilAttachmentDescriptor->name);
272 return isAcceptableFormat(depthStencilAttachmentDescriptor->textureFormat, *iterator->unnamedType, intrinsics, false);
277 Optional<MatchedRenderSemantics> matchSemantics(Program& program, RenderPipelineDescriptor& renderPipelineDescriptor, bool distinctFragmentShader, bool fragmentShaderExists)
279 auto vertexFunctions = program.nameContext().getFunctions(renderPipelineDescriptor.vertexEntryPointName, AST::NameSpace::NameSpace1);
280 if (vertexFunctions.size() != 1 || !vertexFunctions[0].get().entryPointType() || !is<AST::FunctionDefinition>(vertexFunctions[0].get()))
282 auto& vertexShaderEntryPoint = downcast<AST::FunctionDefinition>(vertexFunctions[0].get());
283 auto vertexShaderEntryPointItems = gatherEntryPointItems(program.intrinsics(), vertexShaderEntryPoint);
284 if (!vertexShaderEntryPointItems)
286 auto vertexShaderResourceMap = matchResources(vertexShaderEntryPointItems->inputs, renderPipelineDescriptor.layout, ShaderStage::Vertex);
287 if (!vertexShaderResourceMap)
289 auto matchedVertexAttributes = matchVertexAttributes(vertexShaderEntryPointItems->inputs, renderPipelineDescriptor.vertexAttributes, program.intrinsics());
290 if (!matchedVertexAttributes)
292 if (!fragmentShaderExists)
293 return {{ &vertexShaderEntryPoint, nullptr, *vertexShaderEntryPointItems, EntryPointItems(), *vertexShaderResourceMap, HashMap<Binding*, size_t>(), *matchedVertexAttributes, HashMap<AttachmentDescriptor*, size_t>() }};
295 auto fragmentNameSpace = distinctFragmentShader ? AST::NameSpace::NameSpace2 : AST::NameSpace::NameSpace1;
296 auto fragmentFunctions = program.nameContext().getFunctions(renderPipelineDescriptor.fragmentEntryPointName, fragmentNameSpace);
297 if (fragmentFunctions.size() != 1 || !fragmentFunctions[0].get().entryPointType() || !is<AST::FunctionDefinition>(fragmentFunctions[0].get()))
299 auto& fragmentShaderEntryPoint = downcast<AST::FunctionDefinition>(fragmentFunctions[0].get());
300 auto fragmentShaderEntryPointItems = gatherEntryPointItems(program.intrinsics(), fragmentShaderEntryPoint);
301 if (!fragmentShaderEntryPointItems)
303 auto fragmentShaderResourceMap = matchResources(fragmentShaderEntryPointItems->inputs, renderPipelineDescriptor.layout, ShaderStage::Fragment);
304 if (!fragmentShaderResourceMap)
306 if (!matchInputsOutputs(vertexShaderEntryPointItems->outputs, fragmentShaderEntryPointItems->inputs))
308 auto matchedColorAttachments = matchColorAttachments(fragmentShaderEntryPointItems->outputs, renderPipelineDescriptor.attachmentsStateDescriptor.attachmentDescriptors, program.intrinsics());
309 if (!matchedColorAttachments)
311 if (!matchDepthAttachment(fragmentShaderEntryPointItems->outputs, renderPipelineDescriptor.attachmentsStateDescriptor.depthStencilAttachmentDescriptor, program.intrinsics()))
313 return {{ &vertexShaderEntryPoint, &fragmentShaderEntryPoint, *vertexShaderEntryPointItems, *fragmentShaderEntryPointItems, *vertexShaderResourceMap, *fragmentShaderResourceMap, *matchedVertexAttributes, *matchedColorAttachments }};
316 Optional<MatchedComputeSemantics> matchSemantics(Program& program, ComputePipelineDescriptor& computePipelineDescriptor)
318 auto functions = program.nameContext().getFunctions(computePipelineDescriptor.entryPointName, AST::NameSpace::NameSpace1);
319 if (functions.size() != 1 || !functions[0].get().entryPointType() || !is<AST::FunctionDefinition>(functions[0].get()))
321 auto& entryPoint = downcast<AST::FunctionDefinition>(functions[0].get());
322 auto entryPointItems = gatherEntryPointItems(program.intrinsics(), entryPoint);
323 if (!entryPointItems)
325 ASSERT(entryPointItems->outputs.isEmpty());
326 auto resourceMap = matchResources(entryPointItems->inputs, computePipelineDescriptor.layout, ShaderStage::Compute);
329 return {{ &entryPoint, *entryPointItems, *resourceMap }};
334 } // namespace WebCore
336 #endif // ENABLE(WEBGPU)