blob: f7337c6f02c2a3a1bedf72ba5d237be2d2d3f1a4 [file] [log] [blame]
Ben Claytonf3e2cc22019-11-28 12:02:15 +00001// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "SpirvShader.hpp"
Ben Claytonfc951cd2019-05-15 17:16:56 +010016#include "SpirvShaderDebug.hpp"
Ben Claytonf3e2cc22019-11-28 12:02:15 +000017
18#include "ShaderCore.hpp"
Nicolas Capens9e727fa2021-11-22 12:06:33 -050019#include "Reactor/Assert.hpp"
Ben Claytonf3e2cc22019-11-28 12:02:15 +000020#include "Vulkan/VkPipelineLayout.hpp"
21
22#include <spirv/unified1/spirv.hpp>
Ben Claytonf3e2cc22019-11-28 12:02:15 +000023
24namespace sw {
25
26SpirvShader::EmitResult SpirvShader::EmitLoad(InsnIterator insn, EmitState *state) const
27{
28 bool atomic = (insn.opcode() == spv::OpAtomicLoad);
29 Object::ID resultId = insn.word(2);
30 Object::ID pointerId = insn.word(3);
31 auto &result = getObject(resultId);
Nicolas Capens72f089c2020-04-08 23:37:08 -040032 auto &resultTy = getType(result);
Ben Claytonf3e2cc22019-11-28 12:02:15 +000033 auto &pointer = getObject(pointerId);
Nicolas Capens72f089c2020-04-08 23:37:08 -040034 auto &pointerTy = getType(pointer);
Ben Claytonf3e2cc22019-11-28 12:02:15 +000035 std::memory_order memoryOrder = std::memory_order_relaxed;
36
Nicolas Capens72f089c2020-04-08 23:37:08 -040037 ASSERT(getType(pointer).element == result.typeId());
38 ASSERT(Type::ID(insn.word(1)) == result.typeId());
39 ASSERT(!atomic || getType(getType(pointer).element).opcode() == spv::OpTypeInt); // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
Ben Claytonf3e2cc22019-11-28 12:02:15 +000040
41 if(pointerTy.storageClass == spv::StorageClassUniformConstant)
42 {
43 // Just propagate the pointer.
44 auto &ptr = state->getPointer(pointerId);
45 state->createPointer(resultId, ptr);
46 return EmitResult::Continue;
47 }
48
49 if(atomic)
50 {
51 Object::ID semanticsId = insn.word(5);
52 auto memorySemantics = static_cast<spv::MemorySemanticsMask>(getObject(semanticsId).constantValue[0]);
53 memoryOrder = MemoryOrder(memorySemantics);
54 }
55
56 auto ptr = GetPointerToData(pointerId, 0, state);
57 bool interleavedByLane = IsStorageInterleavedByLane(pointerTy.storageClass);
Nicolas Capensff9f9b52020-04-14 00:46:38 -040058 auto &dst = state->createIntermediate(resultId, resultTy.componentCount);
Alexis Hetu8941bde2021-11-17 17:45:40 -050059 auto robustness = getOutOfBoundsBehavior(pointerId, state);
Ben Claytonf3e2cc22019-11-28 12:02:15 +000060
Ben Claytonbc1c067be2019-12-17 20:37:37 +000061 VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
Ben Clayton18c6a782019-12-03 12:08:16 +000062 auto p = ptr + el.offset;
Nicolas Capens81bc9d92019-12-16 15:05:57 -050063 if(interleavedByLane) { p = InterleaveByLane(p); } // TODO: Interleave once, then add offset?
Ben Clayton18c6a782019-12-03 12:08:16 +000064 dst.move(el.index, p.Load<SIMD::Float>(robustness, state->activeLaneMask(), atomic, memoryOrder));
Ben Claytonf3e2cc22019-11-28 12:02:15 +000065 });
66
Ben Claytonfc951cd2019-05-15 17:16:56 +010067 SPIRV_SHADER_DBG("Load(atomic: {0}, order: {1}, ptr: {2}, val: {3}, mask: {4})", atomic, int(memoryOrder), ptr, dst, state->activeLaneMask());
68
Ben Claytonf3e2cc22019-11-28 12:02:15 +000069 return EmitResult::Continue;
70}
71
72SpirvShader::EmitResult SpirvShader::EmitStore(InsnIterator insn, EmitState *state) const
73{
74 bool atomic = (insn.opcode() == spv::OpAtomicStore);
75 Object::ID pointerId = insn.word(1);
76 Object::ID objectId = insn.word(atomic ? 4 : 2);
Ben Claytonf3e2cc22019-11-28 12:02:15 +000077 std::memory_order memoryOrder = std::memory_order_relaxed;
78
79 if(atomic)
80 {
81 Object::ID semanticsId = insn.word(3);
82 auto memorySemantics = static_cast<spv::MemorySemanticsMask>(getObject(semanticsId).constantValue[0]);
83 memoryOrder = MemoryOrder(memorySemantics);
84 }
85
Nicolas Capens0b77aa52020-04-09 02:48:16 -040086 const auto &value = Operand(this, state, objectId);
87
88 Store(pointerId, value, atomic, memoryOrder, state);
89
90 return EmitResult::Continue;
91}
92
93void SpirvShader::Store(Object::ID pointerId, const Operand &value, bool atomic, std::memory_order memoryOrder, EmitState *state) const
94{
95 auto &pointer = getObject(pointerId);
96 auto &pointerTy = getType(pointer);
97 auto &elementTy = getType(pointerTy.element);
98
Ben Claytonf3e2cc22019-11-28 12:02:15 +000099 ASSERT(!atomic || elementTy.opcode() == spv::OpTypeInt); // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
100
101 auto ptr = GetPointerToData(pointerId, 0, state);
102 bool interleavedByLane = IsStorageInterleavedByLane(pointerTy.storageClass);
Alexis Hetu8941bde2021-11-17 17:45:40 -0500103 auto robustness = getOutOfBoundsBehavior(pointerId, state);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000104
105 SIMD::Int mask = state->activeLaneMask();
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500106 if(!StoresInHelperInvocation(pointerTy.storageClass))
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000107 {
108 mask = mask & state->storesAndAtomicsMask();
109 }
110
Ben Claytonfc951cd2019-05-15 17:16:56 +0100111 SPIRV_SHADER_DBG("Store(atomic: {0}, order: {1}, ptr: {2}, val: {3}, mask: {4}", atomic, int(memoryOrder), ptr, value, mask);
112
Nicolas Capens0b77aa52020-04-09 02:48:16 -0400113 VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
114 auto p = ptr + el.offset;
115 if(interleavedByLane) { p = InterleaveByLane(p); }
116 p.Store(value.Float(el.index), robustness, mask, atomic, memoryOrder);
117 });
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000118}
119
120SpirvShader::EmitResult SpirvShader::EmitVariable(InsnIterator insn, EmitState *state) const
121{
122 auto routine = state->routine;
123 Object::ID resultId = insn.word(2);
124 auto &object = getObject(resultId);
Nicolas Capens72f089c2020-04-08 23:37:08 -0400125 auto &objectTy = getType(object);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000126
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500127 switch(objectTy.storageClass)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000128 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500129 case spv::StorageClassOutput:
130 case spv::StorageClassPrivate:
131 case spv::StorageClassFunction:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000132 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000133 ASSERT(objectTy.opcode() == spv::OpTypePointer);
134 auto base = &routine->getVariable(resultId)[0];
135 auto elementTy = getType(objectTy.element);
Nicolas Capensff9f9b52020-04-14 00:46:38 -0400136 auto size = elementTy.componentCount * static_cast<uint32_t>(sizeof(float)) * SIMD::Width;
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000137 state->createPointer(resultId, SIMD::Pointer(base, size));
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000138 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500139 break;
140 case spv::StorageClassWorkgroup:
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000141 {
142 ASSERT(objectTy.opcode() == spv::OpTypePointer);
143 auto base = &routine->workgroupMemory[0];
144 auto size = workgroupMemory.size();
145 state->createPointer(resultId, SIMD::Pointer(base, size, workgroupMemory.offsetOf(resultId)));
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000146 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500147 break;
148 case spv::StorageClassInput:
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000149 {
150 if(object.kind == Object::Kind::InterfaceVariable)
151 {
152 auto &dst = routine->getVariable(resultId);
153 int offset = 0;
154 VisitInterface(resultId,
155 [&](Decorations const &d, AttribType type) {
156 auto scalarSlot = d.Location << 2 | d.Component;
157 dst[offset++] = routine->inputs[scalarSlot];
158 });
159 }
160 ASSERT(objectTy.opcode() == spv::OpTypePointer);
161 auto base = &routine->getVariable(resultId)[0];
162 auto elementTy = getType(objectTy.element);
Nicolas Capensff9f9b52020-04-14 00:46:38 -0400163 auto size = elementTy.componentCount * static_cast<uint32_t>(sizeof(float)) * SIMD::Width;
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000164 state->createPointer(resultId, SIMD::Pointer(base, size));
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000165 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500166 break;
167 case spv::StorageClassUniformConstant:
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000168 {
169 const auto &d = descriptorDecorations.at(resultId);
170 ASSERT(d.DescriptorSet >= 0);
171 ASSERT(d.Binding >= 0);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000172
Nicolas Capensc7d5ec32020-04-22 01:11:37 -0400173 uint32_t bindingOffset = routine->pipelineLayout->getBindingOffset(d.DescriptorSet, d.Binding);
Nicolas Capensca9de962020-04-23 00:42:39 -0400174 Pointer<Byte> set = routine->descriptorSets[d.DescriptorSet]; // DescriptorSet*
175 Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset); // vk::SampledImageDescriptor*
176 auto size = 0; // Not required as this pointer is not directly used by SIMD::Read or SIMD::Write.
177 state->createPointer(resultId, SIMD::Pointer(binding, size));
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000178 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500179 break;
180 case spv::StorageClassUniform:
181 case spv::StorageClassStorageBuffer:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000182 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000183 const auto &d = descriptorDecorations.at(resultId);
184 ASSERT(d.DescriptorSet >= 0);
185 auto size = 0; // Not required as this pointer is not directly used by SIMD::Read or SIMD::Write.
186 // Note: the module may contain descriptor set references that are not suitable for this implementation -- using a set index higher than the number
187 // of descriptor set binding points we support. As long as the selected entrypoint doesn't actually touch the out of range binding points, this
188 // is valid. In this case make the value nullptr to make it easier to diagnose an attempt to dereference it.
Nicolas Capensb7b7cb72021-09-29 14:02:53 -0400189 if(static_cast<uint32_t>(d.DescriptorSet) < vk::MAX_BOUND_DESCRIPTOR_SETS)
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000190 {
191 state->createPointer(resultId, SIMD::Pointer(routine->descriptorSets[d.DescriptorSet], size));
192 }
193 else
194 {
195 state->createPointer(resultId, SIMD::Pointer(nullptr, 0));
196 }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000197 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500198 break;
199 case spv::StorageClassPushConstant:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000200 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000201 state->createPointer(resultId, SIMD::Pointer(routine->pushConstants, vk::MAX_PUSH_CONSTANT_SIZE));
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000202 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500203 break;
204 default:
205 UNREACHABLE("Storage class %d", objectTy.storageClass);
206 break;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000207 }
208
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500209 if(insn.wordCount() > 4)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000210 {
211 Object::ID initializerId = insn.word(4);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500212 if(getObject(initializerId).kind != Object::Kind::Constant)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000213 {
Nicolas Capensdd0e6002020-01-24 01:21:47 -0500214 UNIMPLEMENTED("b/148241854: Non-constant initializers not yet implemented"); // FIXME(b/148241854)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000215 }
Nicolas Capens44bd43a2020-01-22 03:07:14 -0500216
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500217 switch(objectTy.storageClass)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000218 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500219 case spv::StorageClassOutput:
220 case spv::StorageClassPrivate:
221 case spv::StorageClassFunction:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000222 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000223 bool interleavedByLane = IsStorageInterleavedByLane(objectTy.storageClass);
224 auto ptr = GetPointerToData(resultId, 0, state);
Nicolas Capense6f65d92020-04-08 21:55:43 -0400225 Operand initialValue(this, state, initializerId);
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000226 VisitMemoryObject(resultId, [&](const MemoryElement &el) {
227 auto p = ptr + el.offset;
228 if(interleavedByLane) { p = InterleaveByLane(p); }
229 auto robustness = OutOfBoundsBehavior::UndefinedBehavior; // Local variables are always within bounds.
230 p.Store(initialValue.Float(el.index), robustness, state->activeLaneMask());
231 });
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000232 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500233 break;
234 default:
235 ASSERT_MSG(initializerId == 0, "Vulkan does not permit variables of storage class %d to have initializers", int(objectTy.storageClass));
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000236 }
237 }
238
239 return EmitResult::Continue;
240}
241
242SpirvShader::EmitResult SpirvShader::EmitCopyMemory(InsnIterator insn, EmitState *state) const
243{
244 Object::ID dstPtrId = insn.word(1);
245 Object::ID srcPtrId = insn.word(2);
Nicolas Capense7355b92021-11-08 22:48:34 -0500246 auto &dstPtrTy = getObjectType(dstPtrId);
247 auto &srcPtrTy = getObjectType(srcPtrId);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000248 ASSERT(dstPtrTy.element == srcPtrTy.element);
249
250 bool dstInterleavedByLane = IsStorageInterleavedByLane(dstPtrTy.storageClass);
251 bool srcInterleavedByLane = IsStorageInterleavedByLane(srcPtrTy.storageClass);
252 auto dstPtr = GetPointerToData(dstPtrId, 0, state);
253 auto srcPtr = GetPointerToData(srcPtrId, 0, state);
254
255 std::unordered_map<uint32_t, uint32_t> srcOffsets;
256
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000257 VisitMemoryObject(srcPtrId, [&](const MemoryElement &el) { srcOffsets[el.index] = el.offset; });
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000258
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000259 VisitMemoryObject(dstPtrId, [&](const MemoryElement &el) {
Ben Clayton18c6a782019-12-03 12:08:16 +0000260 auto it = srcOffsets.find(el.index);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000261 ASSERT(it != srcOffsets.end());
262 auto srcOffset = it->second;
Ben Clayton18c6a782019-12-03 12:08:16 +0000263 auto dstOffset = el.offset;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000264
265 auto dst = dstPtr + dstOffset;
266 auto src = srcPtr + srcOffset;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500267 if(dstInterleavedByLane) { dst = InterleaveByLane(dst); }
268 if(srcInterleavedByLane) { src = InterleaveByLane(src); }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000269
270 // TODO(b/131224163): Optimize based on src/dst storage classes.
271 auto robustness = OutOfBoundsBehavior::RobustBufferAccess;
272
273 auto value = src.Load<SIMD::Float>(robustness, state->activeLaneMask());
274 dst.Store(value, robustness, state->activeLaneMask());
275 });
276 return EmitResult::Continue;
277}
278
279SpirvShader::EmitResult SpirvShader::EmitMemoryBarrier(InsnIterator insn, EmitState *state) const
280{
281 auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(2)));
Nicolas Capens4c629802021-12-08 02:05:19 -0500282 // TODO(b/176819536): We probably want to consider the memory scope here.
283 // For now, just always emit the full fence.
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000284 Fence(semantics);
285 return EmitResult::Continue;
286}
287
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000288void SpirvShader::VisitMemoryObjectInner(sw::SpirvShader::Type::ID id, sw::SpirvShader::Decorations d, uint32_t &index, uint32_t offset, const MemoryVisitor &f) const
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000289{
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000290 ApplyDecorationsForId(&d, id);
291 auto const &type = getType(id);
292
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500293 if(d.HasOffset)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000294 {
295 offset += d.Offset;
296 d.HasOffset = false;
297 }
298
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500299 switch(type.opcode())
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000300 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500301 case spv::OpTypePointer:
302 VisitMemoryObjectInner(type.definition.word(3), d, index, offset, f);
303 break;
304 case spv::OpTypeInt:
305 case spv::OpTypeFloat:
306 case spv::OpTypeRuntimeArray:
307 f(MemoryElement{ index++, offset, type });
308 break;
309 case spv::OpTypeVector:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000310 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000311 auto elemStride = (d.InsideMatrix && d.HasRowMajor && d.RowMajor) ? d.MatrixStride : static_cast<int32_t>(sizeof(float));
312 for(auto i = 0u; i < type.definition.word(3); i++)
313 {
314 VisitMemoryObjectInner(type.definition.word(2), d, index, offset + elemStride * i, f);
315 }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000316 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500317 break;
318 case spv::OpTypeMatrix:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000319 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000320 auto columnStride = (d.HasRowMajor && d.RowMajor) ? static_cast<int32_t>(sizeof(float)) : d.MatrixStride;
321 d.InsideMatrix = true;
322 for(auto i = 0u; i < type.definition.word(3); i++)
323 {
324 ASSERT(d.HasMatrixStride);
325 VisitMemoryObjectInner(type.definition.word(2), d, index, offset + columnStride * i, f);
326 }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000327 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500328 break;
329 case spv::OpTypeStruct:
330 for(auto i = 0u; i < type.definition.wordCount() - 2; i++)
331 {
332 ApplyDecorationsForIdMember(&d, id, i);
333 VisitMemoryObjectInner(type.definition.word(i + 2), d, index, offset, f);
334 }
335 break;
336 case spv::OpTypeArray:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000337 {
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000338 auto arraySize = GetConstScalarInt(type.definition.word(3));
339 for(auto i = 0u; i < arraySize; i++)
340 {
341 ASSERT(d.HasArrayStride);
342 VisitMemoryObjectInner(type.definition.word(2), d, index, offset + i * d.ArrayStride, f);
343 }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000344 }
Nicolas Capens112faf42019-12-13 17:32:26 -0500345 break;
346 default:
347 UNREACHABLE("%s", OpcodeName(type.opcode()));
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000348 }
349}
350
Nicolas Capens72f089c2020-04-08 23:37:08 -0400351void SpirvShader::VisitMemoryObject(Object::ID id, const MemoryVisitor &f) const
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000352{
Nicolas Capens72f089c2020-04-08 23:37:08 -0400353 auto typeId = getObject(id).typeId();
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000354 auto const &type = getType(typeId);
Nicolas Capens72f089c2020-04-08 23:37:08 -0400355
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500356 if(IsExplicitLayout(type.storageClass))
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000357 {
358 Decorations d{};
359 ApplyDecorationsForId(&d, id);
360 uint32_t index = 0;
361 VisitMemoryObjectInner(typeId, d, index, 0, f);
362 }
363 else
364 {
365 // Objects without explicit layout are tightly packed.
Ben Clayton18c6a782019-12-03 12:08:16 +0000366 auto &elType = getType(type.element);
Nicolas Capensff9f9b52020-04-14 00:46:38 -0400367 for(auto index = 0u; index < elType.componentCount; index++)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000368 {
Ben Clayton18c6a782019-12-03 12:08:16 +0000369 auto offset = static_cast<uint32_t>(index * sizeof(float));
Ben Claytonbc1c067be2019-12-17 20:37:37 +0000370 f({ index, offset, elType });
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000371 }
372 }
373}
374
Nicolas Capens479d1432020-01-31 11:19:21 -0500375SIMD::Pointer SpirvShader::GetPointerToData(Object::ID id, Int arrayIndex, EmitState const *state) const
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000376{
377 auto routine = state->routine;
378 auto &object = getObject(id);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500379 switch(object.kind)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000380 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500381 case Object::Kind::Pointer:
382 case Object::Kind::InterfaceVariable:
383 return state->getPointer(id);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000384
Nicolas Capens112faf42019-12-13 17:32:26 -0500385 case Object::Kind::DescriptorSet:
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000386 {
387 const auto &d = descriptorDecorations.at(id);
Nicolas Capensb7b7cb72021-09-29 14:02:53 -0400388 ASSERT(d.DescriptorSet >= 0 && static_cast<uint32_t>(d.DescriptorSet) < vk::MAX_BOUND_DESCRIPTOR_SETS);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000389 ASSERT(d.Binding >= 0);
Nicolas Capenseb682442020-06-01 15:24:52 -0400390 ASSERT(routine->pipelineLayout->getDescriptorCount(d.DescriptorSet, d.Binding) != 0); // "If descriptorCount is zero this binding entry is reserved and the resource must not be accessed from any stage via this binding within any pipeline using the set layout."
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000391
Nicolas Capensc7d5ec32020-04-22 01:11:37 -0400392 uint32_t bindingOffset = routine->pipelineLayout->getBindingOffset(d.DescriptorSet, d.Binding);
393 uint32_t descriptorSize = routine->pipelineLayout->getDescriptorSize(d.DescriptorSet, d.Binding);
Nicolas Capens479d1432020-01-31 11:19:21 -0500394 Int descriptorOffset = bindingOffset + descriptorSize * arrayIndex;
Nicolas Capensc7d5ec32020-04-22 01:11:37 -0400395
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000396 auto set = state->getPointer(id);
Nicolas Capens9e727fa2021-11-22 12:06:33 -0500397 Assert(set.base != Pointer<Byte>(nullptr));
Alexis Hetu8941bde2021-11-17 17:45:40 -0500398 Pointer<Byte> descriptor = set.base + descriptorOffset; // BufferDescriptor* or inline uniform block
Nicolas Capensc7d5ec32020-04-22 01:11:37 -0400399
Alexis Hetu8941bde2021-11-17 17:45:40 -0500400 auto descriptorType = routine->pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
401 if(descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000402 {
Alexis Hetu8941bde2021-11-17 17:45:40 -0500403 // Note: there is no bounds checking for inline uniform blocks.
404 // MAX_INLINE_UNIFORM_BLOCK_SIZE represents the maximum size of
405 // an inline uniform block, but this value should remain unused.
406 return SIMD::Pointer(descriptor, vk::MAX_INLINE_UNIFORM_BLOCK_SIZE);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000407 }
408 else
409 {
Alexis Hetu8941bde2021-11-17 17:45:40 -0500410 Pointer<Byte> data = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::BufferDescriptor, ptr)); // void*
411 Int size = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, sizeInBytes));
412
413 if(routine->pipelineLayout->isDescriptorDynamic(d.DescriptorSet, d.Binding))
414 {
415 Int dynamicOffsetIndex =
416 routine->pipelineLayout->getDynamicOffsetIndex(d.DescriptorSet, d.Binding) +
417 arrayIndex;
418 Int offset = routine->descriptorDynamicOffsets[dynamicOffsetIndex];
419 Int robustnessSize = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, robustnessSize));
420
421 return SIMD::Pointer(data + offset, Min(size, robustnessSize - offset));
422 }
423 else
424 {
425 return SIMD::Pointer(data, size);
426 }
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000427 }
428 }
429
Nicolas Capens112faf42019-12-13 17:32:26 -0500430 default:
431 UNREACHABLE("Invalid pointer kind %d", int(object.kind));
432 return SIMD::Pointer(Pointer<Byte>(), 0);
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000433 }
434}
435
Nicolas Capens4c629802021-12-08 02:05:19 -0500436void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
437{
438 if(semantics != spv::MemorySemanticsMaskNone)
439 {
440 rr::Fence(MemoryOrder(semantics));
441 }
442}
443
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000444std::memory_order SpirvShader::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
445{
Nicolas Capens4c629802021-12-08 02:05:19 -0500446 uint32_t control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
447 spv::MemorySemanticsAcquireMask |
448 spv::MemorySemanticsReleaseMask |
449 spv::MemorySemanticsAcquireReleaseMask |
450 spv::MemorySemanticsSequentiallyConsistentMask);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500451 switch(control)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000452 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500453 case spv::MemorySemanticsMaskNone: return std::memory_order_relaxed;
454 case spv::MemorySemanticsAcquireMask: return std::memory_order_acquire;
455 case spv::MemorySemanticsReleaseMask: return std::memory_order_release;
456 case spv::MemorySemanticsAcquireReleaseMask: return std::memory_order_acq_rel;
457 case spv::MemorySemanticsSequentiallyConsistentMask: return std::memory_order_acq_rel; // Vulkan 1.1: "SequentiallyConsistent is treated as AcquireRelease"
458 default:
459 // "it is invalid for more than one of these four bits to be set:
Nicolas Capens4c629802021-12-08 02:05:19 -0500460 // Acquire, Release, AcquireRelease, or SequentiallyConsistent."
Nicolas Capens112faf42019-12-13 17:32:26 -0500461 UNREACHABLE("MemorySemanticsMask: %x", int(control));
462 return std::memory_order_acq_rel;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000463 }
464}
465
466bool SpirvShader::StoresInHelperInvocation(spv::StorageClass storageClass)
467{
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500468 switch(storageClass)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000469 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500470 case spv::StorageClassUniform:
471 case spv::StorageClassStorageBuffer:
472 case spv::StorageClassImage:
473 return false;
474 default:
475 return true;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000476 }
477}
478
479bool SpirvShader::IsExplicitLayout(spv::StorageClass storageClass)
480{
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500481 switch(storageClass)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000482 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500483 case spv::StorageClassUniform:
484 case spv::StorageClassStorageBuffer:
485 case spv::StorageClassPushConstant:
486 return true;
487 default:
488 return false;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000489 }
490}
491
492sw::SIMD::Pointer SpirvShader::InterleaveByLane(sw::SIMD::Pointer p)
493{
494 p *= sw::SIMD::Width;
495 p.staticOffsets[0] += 0 * sizeof(float);
496 p.staticOffsets[1] += 1 * sizeof(float);
497 p.staticOffsets[2] += 2 * sizeof(float);
498 p.staticOffsets[3] += 3 * sizeof(float);
499 return p;
500}
501
502bool SpirvShader::IsStorageInterleavedByLane(spv::StorageClass storageClass)
503{
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500504 switch(storageClass)
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000505 {
Nicolas Capens112faf42019-12-13 17:32:26 -0500506 case spv::StorageClassUniform:
507 case spv::StorageClassStorageBuffer:
508 case spv::StorageClassPushConstant:
509 case spv::StorageClassWorkgroup:
510 case spv::StorageClassImage:
511 return false;
512 default:
513 return true;
Ben Claytonf3e2cc22019-11-28 12:02:15 +0000514 }
515}
516
517} // namespace sw