1*03ce13f7SAndroid Build Coastguard Worker // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2*03ce13f7SAndroid Build Coastguard Worker //
3*03ce13f7SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker //
7*03ce13f7SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker //
9*03ce13f7SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker // limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker
15*03ce13f7SAndroid Build Coastguard Worker #include "SpirvShader.hpp"
16*03ce13f7SAndroid Build Coastguard Worker
17*03ce13f7SAndroid Build Coastguard Worker #include <spirv/unified1/spirv.hpp>
18*03ce13f7SAndroid Build Coastguard Worker
19*03ce13f7SAndroid Build Coastguard Worker namespace sw {
20*03ce13f7SAndroid Build Coastguard Worker
21*03ce13f7SAndroid Build Coastguard Worker // Template function to perform a binary group operation.
22*03ce13f7SAndroid Build Coastguard Worker // |TYPE| should be the type of the binary operation (as a SIMD::<ScalarType>).
23*03ce13f7SAndroid Build Coastguard Worker // |I| should be a type suitable to initialize the identity value.
24*03ce13f7SAndroid Build Coastguard Worker // |APPLY| should be a callable object that takes two RValue<TYPE> parameters
25*03ce13f7SAndroid Build Coastguard Worker // and returns a new RValue<TYPE> corresponding to the operation's result.
26*03ce13f7SAndroid Build Coastguard Worker template<typename TYPE, typename I, typename APPLY>
BinaryOperation(spv::GroupOperation operation,RValue<SIMD::UInt> value,RValue<SIMD::UInt> mask,const I identityValue,APPLY && apply)27*03ce13f7SAndroid Build Coastguard Worker static RValue<TYPE> BinaryOperation(
28*03ce13f7SAndroid Build Coastguard Worker spv::GroupOperation operation,
29*03ce13f7SAndroid Build Coastguard Worker RValue<SIMD::UInt> value,
30*03ce13f7SAndroid Build Coastguard Worker RValue<SIMD::UInt> mask,
31*03ce13f7SAndroid Build Coastguard Worker const I identityValue,
32*03ce13f7SAndroid Build Coastguard Worker APPLY &&apply)
33*03ce13f7SAndroid Build Coastguard Worker {
34*03ce13f7SAndroid Build Coastguard Worker auto identity = TYPE(identityValue);
35*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt v_uint = (value & mask) | (As<SIMD::UInt>(identity) & ~mask);
36*03ce13f7SAndroid Build Coastguard Worker TYPE v = As<TYPE>(v_uint);
37*03ce13f7SAndroid Build Coastguard Worker
38*03ce13f7SAndroid Build Coastguard Worker switch(operation)
39*03ce13f7SAndroid Build Coastguard Worker {
40*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationReduce:
41*03ce13f7SAndroid Build Coastguard Worker {
42*03ce13f7SAndroid Build Coastguard Worker // NOTE: floating-point add and multiply are not really commutative so
43*03ce13f7SAndroid Build Coastguard Worker // ensure that all values in the final lanes are identical
44*03ce13f7SAndroid Build Coastguard Worker TYPE v2 = apply(v.xxzz, v.yyww); // [xy] [xy] [zw] [zw]
45*03ce13f7SAndroid Build Coastguard Worker return apply(v2.xxxx, v2.zzzz); // [xyzw] [xyzw] [xyzw] [xyzw]
46*03ce13f7SAndroid Build Coastguard Worker }
47*03ce13f7SAndroid Build Coastguard Worker break;
48*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationInclusiveScan:
49*03ce13f7SAndroid Build Coastguard Worker {
50*03ce13f7SAndroid Build Coastguard Worker TYPE v2 = apply(v, Shuffle(v, identity, 0x4012) /* [id, v.y, v.z, v.w] */); // [x] [xy] [yz] [zw]
51*03ce13f7SAndroid Build Coastguard Worker return apply(v2, Shuffle(v2, identity, 0x4401) /* [id, id, v2.x, v2.y] */); // [x] [xy] [xyz] [xyzw]
52*03ce13f7SAndroid Build Coastguard Worker }
53*03ce13f7SAndroid Build Coastguard Worker break;
54*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationExclusiveScan:
55*03ce13f7SAndroid Build Coastguard Worker {
56*03ce13f7SAndroid Build Coastguard Worker TYPE v2 = apply(v, Shuffle(v, identity, 0x4012) /* [id, v.y, v.z, v.w] */); // [x] [xy] [yz] [zw]
57*03ce13f7SAndroid Build Coastguard Worker TYPE v3 = apply(v2, Shuffle(v2, identity, 0x4401) /* [id, id, v2.x, v2.y] */); // [x] [xy] [xyz] [xyzw]
58*03ce13f7SAndroid Build Coastguard Worker return Shuffle(v3, identity, 0x4012 /* [id, v3.x, v3.y, v3.z] */); // [i] [x] [xy] [xyz]
59*03ce13f7SAndroid Build Coastguard Worker }
60*03ce13f7SAndroid Build Coastguard Worker break;
61*03ce13f7SAndroid Build Coastguard Worker default:
62*03ce13f7SAndroid Build Coastguard Worker UNSUPPORTED("Group operation: %d", operation);
63*03ce13f7SAndroid Build Coastguard Worker return identity;
64*03ce13f7SAndroid Build Coastguard Worker }
65*03ce13f7SAndroid Build Coastguard Worker }
66*03ce13f7SAndroid Build Coastguard Worker
EmitGroupNonUniform(InsnIterator insn)67*03ce13f7SAndroid Build Coastguard Worker void SpirvEmitter::EmitGroupNonUniform(InsnIterator insn)
68*03ce13f7SAndroid Build Coastguard Worker {
69*03ce13f7SAndroid Build Coastguard Worker ASSERT(SIMD::Width == 4); // EmitGroupNonUniform makes many assumptions that the SIMD vector width is 4
70*03ce13f7SAndroid Build Coastguard Worker
71*03ce13f7SAndroid Build Coastguard Worker auto &type = shader.getType(Type::ID(insn.word(1)));
72*03ce13f7SAndroid Build Coastguard Worker Object::ID resultId = insn.word(2);
73*03ce13f7SAndroid Build Coastguard Worker auto scope = spv::Scope(shader.GetConstScalarInt(insn.word(3)));
74*03ce13f7SAndroid Build Coastguard Worker ASSERT_MSG(scope == spv::ScopeSubgroup, "Scope for Non Uniform Group Operations must be Subgroup for Vulkan 1.1");
75*03ce13f7SAndroid Build Coastguard Worker
76*03ce13f7SAndroid Build Coastguard Worker auto &dst = createIntermediate(resultId, type.componentCount);
77*03ce13f7SAndroid Build Coastguard Worker
78*03ce13f7SAndroid Build Coastguard Worker switch(insn.opcode())
79*03ce13f7SAndroid Build Coastguard Worker {
80*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformElect:
81*03ce13f7SAndroid Build Coastguard Worker {
82*03ce13f7SAndroid Build Coastguard Worker // Result is true only in the active invocation with the lowest id
83*03ce13f7SAndroid Build Coastguard Worker // in the group, otherwise result is false.
84*03ce13f7SAndroid Build Coastguard Worker SIMD::Int active = activeLaneMask(); // Considers helper invocations active. See b/151137030
85*03ce13f7SAndroid Build Coastguard Worker // TODO: Would be nice if we could write this as:
86*03ce13f7SAndroid Build Coastguard Worker // elect = active & ~(active.Oxyz | active.OOxy | active.OOOx)
87*03ce13f7SAndroid Build Coastguard Worker auto v0111 = SIMD::Int(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
88*03ce13f7SAndroid Build Coastguard Worker auto elect = active & ~(v0111 & (active.xxyz | active.xxxy | active.xxxx));
89*03ce13f7SAndroid Build Coastguard Worker dst.move(0, elect);
90*03ce13f7SAndroid Build Coastguard Worker }
91*03ce13f7SAndroid Build Coastguard Worker break;
92*03ce13f7SAndroid Build Coastguard Worker
93*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformAll:
94*03ce13f7SAndroid Build Coastguard Worker {
95*03ce13f7SAndroid Build Coastguard Worker Operand predicate(shader, *this, insn.word(4));
96*03ce13f7SAndroid Build Coastguard Worker dst.move(0, AndAll(predicate.UInt(0) | ~As<SIMD::UInt>(activeLaneMask()))); // Considers helper invocations active. See b/151137030
97*03ce13f7SAndroid Build Coastguard Worker }
98*03ce13f7SAndroid Build Coastguard Worker break;
99*03ce13f7SAndroid Build Coastguard Worker
100*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformAny:
101*03ce13f7SAndroid Build Coastguard Worker {
102*03ce13f7SAndroid Build Coastguard Worker Operand predicate(shader, *this, insn.word(4));
103*03ce13f7SAndroid Build Coastguard Worker dst.move(0, OrAll(predicate.UInt(0) & As<SIMD::UInt>(activeLaneMask()))); // Considers helper invocations active. See b/151137030
104*03ce13f7SAndroid Build Coastguard Worker }
105*03ce13f7SAndroid Build Coastguard Worker break;
106*03ce13f7SAndroid Build Coastguard Worker
107*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformAllEqual:
108*03ce13f7SAndroid Build Coastguard Worker {
109*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(4));
110*03ce13f7SAndroid Build Coastguard Worker auto res = SIMD::UInt(0xffffffff);
111*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt active = As<SIMD::UInt>(activeLaneMask()); // Considers helper invocations active. See b/151137030
112*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt inactive = ~active;
113*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
114*03ce13f7SAndroid Build Coastguard Worker {
115*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt v = value.UInt(i) & active;
116*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt filled = v;
117*03ce13f7SAndroid Build Coastguard Worker for(int j = 0; j < SIMD::Width - 1; j++)
118*03ce13f7SAndroid Build Coastguard Worker {
119*03ce13f7SAndroid Build Coastguard Worker filled |= filled.yzwx & inactive; // Populate inactive 'holes' with a live value
120*03ce13f7SAndroid Build Coastguard Worker }
121*03ce13f7SAndroid Build Coastguard Worker res &= AndAll(CmpEQ(filled.xyzw, filled.yzwx));
122*03ce13f7SAndroid Build Coastguard Worker }
123*03ce13f7SAndroid Build Coastguard Worker dst.move(0, res);
124*03ce13f7SAndroid Build Coastguard Worker }
125*03ce13f7SAndroid Build Coastguard Worker break;
126*03ce13f7SAndroid Build Coastguard Worker
127*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBroadcast:
128*03ce13f7SAndroid Build Coastguard Worker {
129*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
130*03ce13f7SAndroid Build Coastguard Worker auto idId = Object::ID(insn.word(5));
131*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
132*03ce13f7SAndroid Build Coastguard Worker
133*03ce13f7SAndroid Build Coastguard Worker // Decide between the fast path for constants and the slow path for
134*03ce13f7SAndroid Build Coastguard Worker // intermediates.
135*03ce13f7SAndroid Build Coastguard Worker if(shader.getObject(idId).kind == Object::Kind::Constant)
136*03ce13f7SAndroid Build Coastguard Worker {
137*03ce13f7SAndroid Build Coastguard Worker auto id = SIMD::Int(shader.GetConstScalarInt(insn.word(5)));
138*03ce13f7SAndroid Build Coastguard Worker auto mask = CmpEQ(id, SIMD::Int(0, 1, 2, 3));
139*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
140*03ce13f7SAndroid Build Coastguard Worker {
141*03ce13f7SAndroid Build Coastguard Worker dst.move(i, OrAll(value.Int(i) & mask));
142*03ce13f7SAndroid Build Coastguard Worker }
143*03ce13f7SAndroid Build Coastguard Worker }
144*03ce13f7SAndroid Build Coastguard Worker else
145*03ce13f7SAndroid Build Coastguard Worker {
146*03ce13f7SAndroid Build Coastguard Worker Operand id(shader, *this, idId);
147*03ce13f7SAndroid Build Coastguard Worker
148*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt active = As<SIMD::UInt>(activeLaneMask()); // Considers helper invocations active. See b/151137030
149*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt inactive = ~active;
150*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt filled = id.UInt(0) & active;
151*03ce13f7SAndroid Build Coastguard Worker
152*03ce13f7SAndroid Build Coastguard Worker for(int j = 0; j < SIMD::Width - 1; j++)
153*03ce13f7SAndroid Build Coastguard Worker {
154*03ce13f7SAndroid Build Coastguard Worker filled |= filled.yzwx & inactive; // Populate inactive 'holes' with a live value
155*03ce13f7SAndroid Build Coastguard Worker }
156*03ce13f7SAndroid Build Coastguard Worker
157*03ce13f7SAndroid Build Coastguard Worker auto mask = CmpEQ(filled, SIMD::UInt(0, 1, 2, 3));
158*03ce13f7SAndroid Build Coastguard Worker
159*03ce13f7SAndroid Build Coastguard Worker for(uint32_t i = 0u; i < type.componentCount; i++)
160*03ce13f7SAndroid Build Coastguard Worker {
161*03ce13f7SAndroid Build Coastguard Worker dst.move(i, OrAll(value.UInt(i) & mask));
162*03ce13f7SAndroid Build Coastguard Worker }
163*03ce13f7SAndroid Build Coastguard Worker }
164*03ce13f7SAndroid Build Coastguard Worker }
165*03ce13f7SAndroid Build Coastguard Worker break;
166*03ce13f7SAndroid Build Coastguard Worker
167*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBroadcastFirst:
168*03ce13f7SAndroid Build Coastguard Worker {
169*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
170*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
171*03ce13f7SAndroid Build Coastguard Worker // Result is true only in the active invocation with the lowest id
172*03ce13f7SAndroid Build Coastguard Worker // in the group, otherwise result is false.
173*03ce13f7SAndroid Build Coastguard Worker SIMD::Int active = activeLaneMask(); // Considers helper invocations active. See b/151137030
174*03ce13f7SAndroid Build Coastguard Worker // TODO: Would be nice if we could write this as:
175*03ce13f7SAndroid Build Coastguard Worker // elect = active & ~(active.Oxyz | active.OOxy | active.OOOx)
176*03ce13f7SAndroid Build Coastguard Worker auto v0111 = SIMD::Int(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
177*03ce13f7SAndroid Build Coastguard Worker auto elect = active & ~(v0111 & (active.xxyz | active.xxxy | active.xxxx));
178*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
179*03ce13f7SAndroid Build Coastguard Worker {
180*03ce13f7SAndroid Build Coastguard Worker dst.move(i, OrAll(value.Int(i) & elect));
181*03ce13f7SAndroid Build Coastguard Worker }
182*03ce13f7SAndroid Build Coastguard Worker }
183*03ce13f7SAndroid Build Coastguard Worker break;
184*03ce13f7SAndroid Build Coastguard Worker
185*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformQuadBroadcast:
186*03ce13f7SAndroid Build Coastguard Worker {
187*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
188*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
189*03ce13f7SAndroid Build Coastguard Worker
190*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getType(shader.getObject(insn.word(5))).componentCount == 1);
191*03ce13f7SAndroid Build Coastguard Worker auto indexId = Object::ID(insn.word(5));
192*03ce13f7SAndroid Build Coastguard Worker SIMD::Int index = Operand(shader, *this, indexId).Int(0);
193*03ce13f7SAndroid Build Coastguard Worker
194*03ce13f7SAndroid Build Coastguard Worker SIMD::Int active = activeLaneMask();
195*03ce13f7SAndroid Build Coastguard Worker // Populate all lanes in index with the same value. Index is required to be
196*03ce13f7SAndroid Build Coastguard Worker // uniform per the SPIR-V spec, so all active lanes should be identical.
197*03ce13f7SAndroid Build Coastguard Worker index = OrAll(active & index);
198*03ce13f7SAndroid Build Coastguard Worker SIMD::Int mask = CmpEQ(index, SIMD::Int(0, 1, 2, 3));
199*03ce13f7SAndroid Build Coastguard Worker
200*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
201*03ce13f7SAndroid Build Coastguard Worker {
202*03ce13f7SAndroid Build Coastguard Worker dst.move(i, OrAll(value.Int(i) & mask));
203*03ce13f7SAndroid Build Coastguard Worker }
204*03ce13f7SAndroid Build Coastguard Worker }
205*03ce13f7SAndroid Build Coastguard Worker break;
206*03ce13f7SAndroid Build Coastguard Worker
207*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformQuadSwap:
208*03ce13f7SAndroid Build Coastguard Worker {
209*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
210*03ce13f7SAndroid Build Coastguard Worker // SPIR-V spec: Drection must be a scalar of integer type and come from a constant instruction
211*03ce13f7SAndroid Build Coastguard Worker int direction = shader.GetConstScalarInt(insn.word(5));
212*03ce13f7SAndroid Build Coastguard Worker
213*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
214*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
215*03ce13f7SAndroid Build Coastguard Worker {
216*03ce13f7SAndroid Build Coastguard Worker SIMD::Int v = value.Int(i);
217*03ce13f7SAndroid Build Coastguard Worker switch(direction)
218*03ce13f7SAndroid Build Coastguard Worker {
219*03ce13f7SAndroid Build Coastguard Worker case 0: // Horizontal
220*03ce13f7SAndroid Build Coastguard Worker dst.move(i, v.yxwz);
221*03ce13f7SAndroid Build Coastguard Worker break;
222*03ce13f7SAndroid Build Coastguard Worker case 1: // Vertical
223*03ce13f7SAndroid Build Coastguard Worker dst.move(i, v.zwxy);
224*03ce13f7SAndroid Build Coastguard Worker break;
225*03ce13f7SAndroid Build Coastguard Worker case 2: // Diagonal
226*03ce13f7SAndroid Build Coastguard Worker dst.move(i, v.wzyx);
227*03ce13f7SAndroid Build Coastguard Worker break;
228*03ce13f7SAndroid Build Coastguard Worker default:
229*03ce13f7SAndroid Build Coastguard Worker // The SPIR-V spec doesn't define what happens in this case,
230*03ce13f7SAndroid Build Coastguard Worker // so the result in undefined.
231*03ce13f7SAndroid Build Coastguard Worker UNSUPPORTED("SPIR-V does not define a OpGroupNonUniformQuadSwap result for a direction of %d", direction);
232*03ce13f7SAndroid Build Coastguard Worker break;
233*03ce13f7SAndroid Build Coastguard Worker }
234*03ce13f7SAndroid Build Coastguard Worker }
235*03ce13f7SAndroid Build Coastguard Worker }
236*03ce13f7SAndroid Build Coastguard Worker break;
237*03ce13f7SAndroid Build Coastguard Worker
238*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBallot:
239*03ce13f7SAndroid Build Coastguard Worker {
240*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 4);
241*03ce13f7SAndroid Build Coastguard Worker Operand predicate(shader, *this, insn.word(4));
242*03ce13f7SAndroid Build Coastguard Worker dst.move(0, SIMD::Int(SignMask(activeLaneMask() & predicate.Int(0)))); // Considers helper invocations active. See b/151137030
243*03ce13f7SAndroid Build Coastguard Worker dst.move(1, SIMD::Int(0));
244*03ce13f7SAndroid Build Coastguard Worker dst.move(2, SIMD::Int(0));
245*03ce13f7SAndroid Build Coastguard Worker dst.move(3, SIMD::Int(0));
246*03ce13f7SAndroid Build Coastguard Worker }
247*03ce13f7SAndroid Build Coastguard Worker break;
248*03ce13f7SAndroid Build Coastguard Worker
249*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformInverseBallot:
250*03ce13f7SAndroid Build Coastguard Worker {
251*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
252*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 1);
253*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(valueId).componentCount == 4);
254*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
255*03ce13f7SAndroid Build Coastguard Worker auto bit = (value.Int(0) >> SIMD::Int(0, 1, 2, 3)) & SIMD::Int(1);
256*03ce13f7SAndroid Build Coastguard Worker dst.move(0, -bit);
257*03ce13f7SAndroid Build Coastguard Worker }
258*03ce13f7SAndroid Build Coastguard Worker break;
259*03ce13f7SAndroid Build Coastguard Worker
260*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBallotBitExtract:
261*03ce13f7SAndroid Build Coastguard Worker {
262*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
263*03ce13f7SAndroid Build Coastguard Worker auto indexId = Object::ID(insn.word(5));
264*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 1);
265*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(valueId).componentCount == 4);
266*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(indexId).componentCount == 1);
267*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
268*03ce13f7SAndroid Build Coastguard Worker Operand index(shader, *this, indexId);
269*03ce13f7SAndroid Build Coastguard Worker auto vecIdx = index.Int(0) / SIMD::Int(32);
270*03ce13f7SAndroid Build Coastguard Worker auto bitIdx = index.Int(0) & SIMD::Int(31);
271*03ce13f7SAndroid Build Coastguard Worker auto bits = (value.Int(0) & CmpEQ(vecIdx, SIMD::Int(0))) |
272*03ce13f7SAndroid Build Coastguard Worker (value.Int(1) & CmpEQ(vecIdx, SIMD::Int(1))) |
273*03ce13f7SAndroid Build Coastguard Worker (value.Int(2) & CmpEQ(vecIdx, SIMD::Int(2))) |
274*03ce13f7SAndroid Build Coastguard Worker (value.Int(3) & CmpEQ(vecIdx, SIMD::Int(3)));
275*03ce13f7SAndroid Build Coastguard Worker dst.move(0, -((bits >> bitIdx) & SIMD::Int(1)));
276*03ce13f7SAndroid Build Coastguard Worker }
277*03ce13f7SAndroid Build Coastguard Worker break;
278*03ce13f7SAndroid Build Coastguard Worker
279*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBallotBitCount:
280*03ce13f7SAndroid Build Coastguard Worker {
281*03ce13f7SAndroid Build Coastguard Worker auto operation = spv::GroupOperation(insn.word(4));
282*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(5));
283*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 1);
284*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(valueId).componentCount == 4);
285*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
286*03ce13f7SAndroid Build Coastguard Worker switch(operation)
287*03ce13f7SAndroid Build Coastguard Worker {
288*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationReduce:
289*03ce13f7SAndroid Build Coastguard Worker dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(15)));
290*03ce13f7SAndroid Build Coastguard Worker break;
291*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationInclusiveScan:
292*03ce13f7SAndroid Build Coastguard Worker dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(1, 3, 7, 15)));
293*03ce13f7SAndroid Build Coastguard Worker break;
294*03ce13f7SAndroid Build Coastguard Worker case spv::GroupOperationExclusiveScan:
295*03ce13f7SAndroid Build Coastguard Worker dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(0, 1, 3, 7)));
296*03ce13f7SAndroid Build Coastguard Worker break;
297*03ce13f7SAndroid Build Coastguard Worker default:
298*03ce13f7SAndroid Build Coastguard Worker UNSUPPORTED("GroupOperation %d", int(operation));
299*03ce13f7SAndroid Build Coastguard Worker }
300*03ce13f7SAndroid Build Coastguard Worker }
301*03ce13f7SAndroid Build Coastguard Worker break;
302*03ce13f7SAndroid Build Coastguard Worker
303*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBallotFindLSB:
304*03ce13f7SAndroid Build Coastguard Worker {
305*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
306*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 1);
307*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(valueId).componentCount == 4);
308*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
309*03ce13f7SAndroid Build Coastguard Worker dst.move(0, Cttz(value.UInt(0) & SIMD::UInt(15), false));
310*03ce13f7SAndroid Build Coastguard Worker }
311*03ce13f7SAndroid Build Coastguard Worker break;
312*03ce13f7SAndroid Build Coastguard Worker
313*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBallotFindMSB:
314*03ce13f7SAndroid Build Coastguard Worker {
315*03ce13f7SAndroid Build Coastguard Worker auto valueId = Object::ID(insn.word(4));
316*03ce13f7SAndroid Build Coastguard Worker ASSERT(type.componentCount == 1);
317*03ce13f7SAndroid Build Coastguard Worker ASSERT(shader.getObjectType(valueId).componentCount == 4);
318*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, valueId);
319*03ce13f7SAndroid Build Coastguard Worker dst.move(0, SIMD::UInt(31) - Ctlz(value.UInt(0) & SIMD::UInt(15), false));
320*03ce13f7SAndroid Build Coastguard Worker }
321*03ce13f7SAndroid Build Coastguard Worker break;
322*03ce13f7SAndroid Build Coastguard Worker
323*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformShuffle:
324*03ce13f7SAndroid Build Coastguard Worker {
325*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(4));
326*03ce13f7SAndroid Build Coastguard Worker Operand id(shader, *this, insn.word(5));
327*03ce13f7SAndroid Build Coastguard Worker auto x = CmpEQ(SIMD::Int(0), id.Int(0));
328*03ce13f7SAndroid Build Coastguard Worker auto y = CmpEQ(SIMD::Int(1), id.Int(0));
329*03ce13f7SAndroid Build Coastguard Worker auto z = CmpEQ(SIMD::Int(2), id.Int(0));
330*03ce13f7SAndroid Build Coastguard Worker auto w = CmpEQ(SIMD::Int(3), id.Int(0));
331*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
332*03ce13f7SAndroid Build Coastguard Worker {
333*03ce13f7SAndroid Build Coastguard Worker SIMD::Int v = value.Int(i);
334*03ce13f7SAndroid Build Coastguard Worker dst.move(i, (x & v.xxxx) | (y & v.yyyy) | (z & v.zzzz) | (w & v.wwww));
335*03ce13f7SAndroid Build Coastguard Worker }
336*03ce13f7SAndroid Build Coastguard Worker }
337*03ce13f7SAndroid Build Coastguard Worker break;
338*03ce13f7SAndroid Build Coastguard Worker
339*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformShuffleXor:
340*03ce13f7SAndroid Build Coastguard Worker {
341*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(4));
342*03ce13f7SAndroid Build Coastguard Worker Operand mask(shader, *this, insn.word(5));
343*03ce13f7SAndroid Build Coastguard Worker auto x = CmpEQ(SIMD::Int(0), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
344*03ce13f7SAndroid Build Coastguard Worker auto y = CmpEQ(SIMD::Int(1), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
345*03ce13f7SAndroid Build Coastguard Worker auto z = CmpEQ(SIMD::Int(2), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
346*03ce13f7SAndroid Build Coastguard Worker auto w = CmpEQ(SIMD::Int(3), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
347*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
348*03ce13f7SAndroid Build Coastguard Worker {
349*03ce13f7SAndroid Build Coastguard Worker SIMD::Int v = value.Int(i);
350*03ce13f7SAndroid Build Coastguard Worker dst.move(i, (x & v.xxxx) | (y & v.yyyy) | (z & v.zzzz) | (w & v.wwww));
351*03ce13f7SAndroid Build Coastguard Worker }
352*03ce13f7SAndroid Build Coastguard Worker }
353*03ce13f7SAndroid Build Coastguard Worker break;
354*03ce13f7SAndroid Build Coastguard Worker
355*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformShuffleUp:
356*03ce13f7SAndroid Build Coastguard Worker {
357*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(4));
358*03ce13f7SAndroid Build Coastguard Worker Operand delta(shader, *this, insn.word(5));
359*03ce13f7SAndroid Build Coastguard Worker auto d0 = CmpEQ(SIMD::Int(0), delta.Int(0));
360*03ce13f7SAndroid Build Coastguard Worker auto d1 = CmpEQ(SIMD::Int(1), delta.Int(0));
361*03ce13f7SAndroid Build Coastguard Worker auto d2 = CmpEQ(SIMD::Int(2), delta.Int(0));
362*03ce13f7SAndroid Build Coastguard Worker auto d3 = CmpEQ(SIMD::Int(3), delta.Int(0));
363*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
364*03ce13f7SAndroid Build Coastguard Worker {
365*03ce13f7SAndroid Build Coastguard Worker SIMD::Int v = value.Int(i);
366*03ce13f7SAndroid Build Coastguard Worker dst.move(i, (d0 & v.xyzw) | (d1 & v.xxyz) | (d2 & v.xxxy) | (d3 & v.xxxx));
367*03ce13f7SAndroid Build Coastguard Worker }
368*03ce13f7SAndroid Build Coastguard Worker }
369*03ce13f7SAndroid Build Coastguard Worker break;
370*03ce13f7SAndroid Build Coastguard Worker
371*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformShuffleDown:
372*03ce13f7SAndroid Build Coastguard Worker {
373*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(4));
374*03ce13f7SAndroid Build Coastguard Worker Operand delta(shader, *this, insn.word(5));
375*03ce13f7SAndroid Build Coastguard Worker auto d0 = CmpEQ(SIMD::Int(0), delta.Int(0));
376*03ce13f7SAndroid Build Coastguard Worker auto d1 = CmpEQ(SIMD::Int(1), delta.Int(0));
377*03ce13f7SAndroid Build Coastguard Worker auto d2 = CmpEQ(SIMD::Int(2), delta.Int(0));
378*03ce13f7SAndroid Build Coastguard Worker auto d3 = CmpEQ(SIMD::Int(3), delta.Int(0));
379*03ce13f7SAndroid Build Coastguard Worker for(auto i = 0u; i < type.componentCount; i++)
380*03ce13f7SAndroid Build Coastguard Worker {
381*03ce13f7SAndroid Build Coastguard Worker SIMD::Int v = value.Int(i);
382*03ce13f7SAndroid Build Coastguard Worker dst.move(i, (d0 & v.xyzw) | (d1 & v.yzww) | (d2 & v.zwww) | (d3 & v.wwww));
383*03ce13f7SAndroid Build Coastguard Worker }
384*03ce13f7SAndroid Build Coastguard Worker }
385*03ce13f7SAndroid Build Coastguard Worker break;
386*03ce13f7SAndroid Build Coastguard Worker
387*03ce13f7SAndroid Build Coastguard Worker // The remaining instructions are GroupNonUniformArithmetic operations
388*03ce13f7SAndroid Build Coastguard Worker default:
389*03ce13f7SAndroid Build Coastguard Worker auto &type = shader.getType(Type::ID(insn.word(1)));
390*03ce13f7SAndroid Build Coastguard Worker auto operation = static_cast<spv::GroupOperation>(insn.word(4));
391*03ce13f7SAndroid Build Coastguard Worker Operand value(shader, *this, insn.word(5));
392*03ce13f7SAndroid Build Coastguard Worker auto mask = As<SIMD::UInt>(activeLaneMask()); // Considers helper invocations active. See b/151137030
393*03ce13f7SAndroid Build Coastguard Worker
394*03ce13f7SAndroid Build Coastguard Worker for(uint32_t i = 0; i < type.componentCount; i++)
395*03ce13f7SAndroid Build Coastguard Worker {
396*03ce13f7SAndroid Build Coastguard Worker switch(insn.opcode())
397*03ce13f7SAndroid Build Coastguard Worker {
398*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformIAdd:
399*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Int>(
400*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
401*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a + b; }));
402*03ce13f7SAndroid Build Coastguard Worker break;
403*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformFAdd:
404*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Float>(
405*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0.0f,
406*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a + b; }));
407*03ce13f7SAndroid Build Coastguard Worker break;
408*03ce13f7SAndroid Build Coastguard Worker
409*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformIMul:
410*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Int>(
411*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 1,
412*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a * b; }));
413*03ce13f7SAndroid Build Coastguard Worker break;
414*03ce13f7SAndroid Build Coastguard Worker
415*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformFMul:
416*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Float>(
417*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 1.0f,
418*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a * b; }));
419*03ce13f7SAndroid Build Coastguard Worker break;
420*03ce13f7SAndroid Build Coastguard Worker
421*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBitwiseAnd:
422*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
423*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, ~0u,
424*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a & b; }));
425*03ce13f7SAndroid Build Coastguard Worker break;
426*03ce13f7SAndroid Build Coastguard Worker
427*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBitwiseOr:
428*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
429*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
430*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a | b; }));
431*03ce13f7SAndroid Build Coastguard Worker break;
432*03ce13f7SAndroid Build Coastguard Worker
433*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformBitwiseXor:
434*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
435*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
436*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return a ^ b; }));
437*03ce13f7SAndroid Build Coastguard Worker break;
438*03ce13f7SAndroid Build Coastguard Worker
439*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformSMin:
440*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Int>(
441*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, INT32_MAX,
442*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return Min(a, b); }));
443*03ce13f7SAndroid Build Coastguard Worker break;
444*03ce13f7SAndroid Build Coastguard Worker
445*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformUMin:
446*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
447*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, ~0u,
448*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return Min(a, b); }));
449*03ce13f7SAndroid Build Coastguard Worker break;
450*03ce13f7SAndroid Build Coastguard Worker
451*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformFMin:
452*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Float>(
453*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, SIMD::Float::infinity(),
454*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return NMin(a, b); }));
455*03ce13f7SAndroid Build Coastguard Worker break;
456*03ce13f7SAndroid Build Coastguard Worker
457*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformSMax:
458*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Int>(
459*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, INT32_MIN,
460*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return Max(a, b); }));
461*03ce13f7SAndroid Build Coastguard Worker break;
462*03ce13f7SAndroid Build Coastguard Worker
463*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformUMax:
464*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
465*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
466*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return Max(a, b); }));
467*03ce13f7SAndroid Build Coastguard Worker break;
468*03ce13f7SAndroid Build Coastguard Worker
469*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformFMax:
470*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::Float>(
471*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, -SIMD::Float::infinity(),
472*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) { return NMax(a, b); }));
473*03ce13f7SAndroid Build Coastguard Worker break;
474*03ce13f7SAndroid Build Coastguard Worker
475*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformLogicalAnd:
476*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
477*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, ~0u,
478*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) {
479*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt zero = SIMD::UInt(0);
480*03ce13f7SAndroid Build Coastguard Worker return CmpNEQ(a, zero) & CmpNEQ(b, zero);
481*03ce13f7SAndroid Build Coastguard Worker }));
482*03ce13f7SAndroid Build Coastguard Worker break;
483*03ce13f7SAndroid Build Coastguard Worker
484*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformLogicalOr:
485*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
486*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
487*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) {
488*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt zero = SIMD::UInt(0);
489*03ce13f7SAndroid Build Coastguard Worker return CmpNEQ(a, zero) | CmpNEQ(b, zero);
490*03ce13f7SAndroid Build Coastguard Worker }));
491*03ce13f7SAndroid Build Coastguard Worker break;
492*03ce13f7SAndroid Build Coastguard Worker
493*03ce13f7SAndroid Build Coastguard Worker case spv::OpGroupNonUniformLogicalXor:
494*03ce13f7SAndroid Build Coastguard Worker dst.move(i, BinaryOperation<SIMD::UInt>(
495*03ce13f7SAndroid Build Coastguard Worker operation, value.UInt(i), mask, 0,
496*03ce13f7SAndroid Build Coastguard Worker [](auto a, auto b) {
497*03ce13f7SAndroid Build Coastguard Worker SIMD::UInt zero = SIMD::UInt(0);
498*03ce13f7SAndroid Build Coastguard Worker return CmpNEQ(a, zero) ^ CmpNEQ(b, zero);
499*03ce13f7SAndroid Build Coastguard Worker }));
500*03ce13f7SAndroid Build Coastguard Worker break;
501*03ce13f7SAndroid Build Coastguard Worker
502*03ce13f7SAndroid Build Coastguard Worker default:
503*03ce13f7SAndroid Build Coastguard Worker UNSUPPORTED("EmitGroupNonUniform op: %s", shader.OpcodeName(type.opcode()));
504*03ce13f7SAndroid Build Coastguard Worker }
505*03ce13f7SAndroid Build Coastguard Worker }
506*03ce13f7SAndroid Build Coastguard Worker break;
507*03ce13f7SAndroid Build Coastguard Worker }
508*03ce13f7SAndroid Build Coastguard Worker }
509*03ce13f7SAndroid Build Coastguard Worker
510*03ce13f7SAndroid Build Coastguard Worker } // namespace sw
511