diff --git a/20151123.html b/20151123.html new file mode 100644 index 0000000..59f7b6a --- /dev/null +++ b/20151123.html @@ -0,0 +1,191 @@ +
+

20151123 - Cross-Invocation Data Sharing Portability

+
+ +A general look at the possibility of portability for dGPUs with regards to cross-invocation data sharing +(aka to go next after ARB_shader_ballot which starts exposing useful SIMD-level programming constructs). +As always I'd like any feedback anyone has on this topic, feel free to write comments or contact me directly. +Warning this was typed up fast to collect ideas, might be some errors in here...
+
+References: +NV_shader_thread_group | +NV_shader_thread_shuffle | +AMD GCN3 ISA Docs
+
+ +NV Quad Swizzle (supported on Fermi and beyond) +
+shuffledData = quadSwizzle{mode}NV({type} data, [{type} operand])
+Where,
+(1.) "mode" is {0,1,2,3,X,Y}
+(2.) "type" must be a floating point type (implies possible NaN issues issues with integers)
+(3.) "operand" is an optional extra unshuffled operand which can be added to the result
+The "mode" is either a direct index into the 2x2 fragment quad, or a swap in the X or Y directions.
+
+AMD GCN DS_SWIZZLE_B32 +
+swizzledData = quadSwizzleAMD({type} data, mode)
+Where,
+(1.) "mode" is a bit array, can be any permutation (not limited to just what NVIDIA exposes)
+(2.) "type" can be integer or floating point
+
+Possible Portable Swizzle Interface +
+bool allQuad(bool value) // returns true if all invocations in quad are true
+bool anyQuad(bool value) // returns true for entire quad if any invocations are true
+swizzledData = quadSwizzleFloat{mode}({type} data)
+swizzledData = quadSwizzle{mode}({type} data)
+Where,
+(1.) "mode" is the portable subset {0,1,2,3,X,Y} (same as NV)
+(2.) "type" is limited to float based types only for quadSwizzleFloat()
+This is the direct union of common functionality from both dGPU vendors. +NV's swizzled data returns 0 for "swizzledData" if any invocation in the quad is inactive according to the GL extension. +AMD returns 0 for "swizzledData" only for inactive invocations. +So the portable spec would have undefined results for "swizzledData" if any invocation in the fragment quad is inactive. +This is a perfectly acceptable compromise IMO. +Would work on all AMD GCN GPUs and any NVIDIA GPU since Fermi for quadSwizzlefloat(), and since Maxwell for quadSwizzle() (using shuffle, see below), +this implies two extensions. +Quads in non fragment shaders are defined by directly splitting the SIMD vector into aligned groups of 4 invocations.
+
+
+
+NV Shuffle (supported starting with Maxwell) +
+shuffledData = shuffle{mode}NV({type} data, uint index, uint width, [out bool valid])
+Where,
+(1.) "mode" is one of {up, down, xor, indexed}
+(2.) "data" is what to shuffle
+(3.) "index" is a invocation index in the SIMD vector (0 to 31 on NV GPUs)
+(4.) "width" is {2,4,8,16, or 32}, divides the SIMD vector into equal sized segments
+(5.) "valid" is optional return which is false if the shuffle was out-of-segment
+Below the "startOfSegmentIndex" is the invocation index of where the segment starts in the SIMD vector. +The "selfIndex" is the invocation's own index in the SIMD vector. +Each invocation computes a "shuffleIndex" of another invocation to read "data" from, then returns the read "data". +Out-of-segment means that "shuffleIndex" is out of the local segment defined by "width". +Out-of-segment shuffles result in "valid = false" and sets "shuffleIndex = selfIndex" (to return un-shuffled "data"). +The computation of "shuffleIndex" before the out-of-segment check depends on "mode".
+ +(indexed) shuffleIndex = startOfSegmentIndex + index
+(_____up) shuffleIndex = selfIndex - index
+(___down) shuffleIndex = selfIndex + index
+(____xor) shuffleIndex = selfIndex ^ index

+
+ +AMD DS_SWIZZLE_B32 (all GCN) +
+Also can do swizzle across segments of 32 invocations using the following math.
+ +and_mask = offset[4:0];
+or_mask = offset[9:5];
+xor_mask = offset[14:10];
+for (i = 0; i < 32; i++) {
+j = ((i & and_mask) | or_mask) ^ xor_mask;
+thread_out[i] = thread_valid[j] ? thread_in[j] : 0; }

+The "_mask" values are compile time immediate values encoded into the instruction.
+
+AMD VOP_DPP (starts with GCN3: Tonga, Fiji, etc) +
+DPP can do many things,
+For a segment size of 4, can do full permutation by immediate operand.
+For a segment size of 16, can shift invocations left by an immediate operand count.
+For a segment size of 16, can shift invocations right by an immediate operand count.
+For a segment size of 16, can rotation invocations right by and immediate operand count.
+For a segment size of 64, can shift or rotate, left or right, by 1 invocation.
+For a segment size of 16, can reverse the order of invocations.
+For a segment size of 8, can reverse the order of invocations.
+For a segment size of 16, can broadcast the 15th segment invocation to fill the next segment.
+Can broadcast invocation 31 to all invocations after 31.
+Has option of either using "selfIndex" on out-of-segment, or forcing return of zero.
+Has option to force on invocations for the operation.
+
+AMD DS_PERMUTE_B32 / DS_BPERMUTE_B32 (starts with GCN3: Tonga, Fiji, etc) +
+Supports something like this (where "temp" is in hardware),
+bpermute(data, uint index) { temp[selfIndex] = data; return temp[index]; }
+permute(data, uint index) { temp[index] = data; return temp[selfIndex]; }
+
+
+ +Possible Portable Shuffle Interface : AMD GCN + NV Maxwell +
+This is just a start of ideas, have not had time to fully explore the options, feedback welcomed...
+SIMD width would be different for each platform so developer would need to build shader permutations for different platform SIMD width in some cases.
+
+SEGMENT-WIDE BUTTERFLY
+butterflyData = butterfly{width}({type} data)
+Where "width" is {2,4,8,16,32}. +This is "xor" mode for shuffle on NV, and DS_SWIZZLE_B32 on AMD (with and_mask = ~0, and or_mask = 0) with possible DPP optimizations on GCN3 for "width"={2 or 4}. +The XOR "mask" field for both NV and AMD is "width>>1". +This can be used to implement a bitonic sort (see slide 19 here).
+
+SEGMENT-WIDE PARALLEL SCAN
+TODO: Weather is nice outside, will write up later...
+
+ +SEGMENT-WIDE PARALLEL REDUCTIONS
+reducedData = reduce{op}{width}({type} data)
+Where,
+(1.) "op" specifies the operation to use in the reduction (add, min, max, and, ... etc)
+(2.) "width" specifies the segment width
+At the end of this operation only the largest indexed invocation in each segment has the result, +the values for all other invocations in the segment are undefined. +This enables both NV and AMD to have optimal paths. +This uses "up" or "xor" mode on NV for log2("width") operations. +Implementation on AMD GCN uses DS_SWIZZLE_B32 as follows,
+ +32 to 16 => DS_SWIZZLE_B32 and_mask=31, or_mask=0, xor_mask=16
+16 to 8 => DS_SWIZZLE_B32 and_mask=31, or_mask=0, xor_mask=8
+8 to 4 => DS_SWIZZLE_B32 and_mask=31, or_mask=0, xor_mask=4
+4 to 2 => DS_SWIZZLE_B32 and_mask=31, or_mask=0, xor_mask=2
+2 to 1 => DS_SWIZZLE_B32 and_mask=31, or_mask=0, xor_mask=1
+64 from finalized 32 => V_READFIRSTLANE_B32 to grab invocation 0 to apply to all invocations

+Implementation on AMD GCN3 uses DPP as follows,
+ +16 to 8 => reverse order of 16-wide (DPP_ROW_MIRROR)
+__0123456789abcdef__
+__fedcba9876543210__
+8 to 4 => reverse order of 8-wide (DPP_ROW_HALF_MIRROR)
+_01234567_
+_76543210_
+4 to 2 => reverse order using full 4-wide permutation mode
+__0123__
+__3210__
+2 to 1 => reverse order using full 4-wide permutation mode
+__0123__
+__1032__
+32 from finalized 16 => DPP_ROW_BCAST15
+__...............s...............t...............u................__
+__................ssssssssssssssssttttttttttttttttuuuuuuuuuuuuuuuu__
+64 from finalized 32 => DPP_ROW_BCAST32
+__...............................s................................__
+__................................ssssssssssssssssssssssssssssssss__
+

+reducedData = allReduce{op}{width}({type} data)
+The difference being that all invocations end up with the result. +Uses "xor" mode on NV for log2("width") operations. +On AMD this is the same as "reduce" except for "width"={32 or 64}. +The 64 case can use V_READLANE_B32 from the "reduce" version to keep the result in an SGPR to save from using a VGPR. +The 32 case can use DS_SWIZZLE_B32 for the 32 to 16 step. +
+
+
+ + + +Possible Portable Shuffle Interface 2nd Extension : AMD GCN3 + NV Maxwell +
+This is just a start of ideas, have not had time to fully explore the options, feedback welcomed...
+SIMD width would be different for each platform so developer would need to build shader permutations for different platform SIMD width in various cases.
+
+SIMD-WIDE PERMUTE
+Backwards permutation of full SIMD width is portable across platforms, maps on NV to shuffleNV(data, index, 32), and DS_BPERMUTE_B32 on AMD,
+permutedData = bpermute(data, index)
+ + + + + + +
+ + diff --git a/index.html b/index.html index dc8057e..7b1d803 100644 --- a/index.html +++ b/index.html @@ -25,7 +25,7 @@ O OOO ||..||OO||||||||||OOo || ||||||||||||| ||||OO|| |o| ||||||||OO||OOO .. .. . .// . ... /

 
-

20161112 - Index - RSS

+

20161115 - Index - RSS



@@ -53,7 +53,7 @@ and attempting to restore what I can from prior lost images.

@@ -119,6 +119,7 @@ and attempting to restore what I can from prior lost images. 2015
20151222 - Random Holiday 2015

+20151123 - Cross-Invocation Data Sharing Portability
20151122 - CRT Inventory
20151121 - ISA Toolbox
20151116 - Mixing Temporal AA and Transparency