diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.cpp b/renderdoc/driver/shaders/spirv/spirv_debug.cpp index a8511c65d..d141ff461 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug.cpp @@ -26,6 +26,7 @@ #include #include #include "common/formatting.h" +#include "os/os_specific.h" #include "spirv_op_helpers.h" static bool ContainsNaNInf(const ShaderVariable &val) @@ -1202,6 +1203,85 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray // ////////////////////////////////////////////////////////////////////////////// + case Op::BitCount: + { + OpBitCount bitwise(it); + + ShaderVariable var = GetSrc(bitwise.base); + + for(uint8_t c = 0; c < var.columns; c++) + var.value.uv[c] = Bits::CountOnes(var.value.uv[c]); + + SetDst(bitwise.result, var); + break; + } + case Op::BitReverse: + { + OpBitReverse bitwise(it); + + ShaderVariable var = GetSrc(bitwise.base); + + for(uint8_t c = 0; c < var.columns; c++) + { + uint32_t u = var.value.uv[c]; + var.value.uv[c] = 0; + for(uint8_t b = 0; b < 32; b++) + { + uint32_t bit = u & (1u << b); + var.value.uv[c] |= bit << (31 - b); + } + } + + SetDst(bitwise.result, var); + break; + } + case Op::BitFieldUExtract: + case Op::BitFieldSExtract: + { + OpBitFieldUExtract bitwise(it); + + ShaderVariable var = GetSrc(bitwise.base); + ShaderVariable offset = GetSrc(bitwise.offset); + ShaderVariable count = GetSrc(bitwise.count); + + for(uint8_t c = 0; c < var.columns; c++) + { + const uint32_t mask = (1u << count.value.uv[c]) - 1; + + var.value.uv[c] >>= offset.value.uv[c]; + var.value.uv[c] &= (1u << count.value.uv[c]) - 1; + + if(opdata.op == Op::BitFieldSExtract) + { + uint32_t topbit = (mask + 1u) >> 1u; + if(var.value.uv[c] & topbit) + var.value.uv[c] |= (0xffffffffu ^ mask); + } + } + + SetDst(bitwise.result, var); + break; + } + case Op::BitFieldInsert: + { + OpBitFieldInsert bitwise(it); + + ShaderVariable var = GetSrc(bitwise.base); + ShaderVariable insert = GetSrc(bitwise.insert); + ShaderVariable offset = GetSrc(bitwise.offset); + ShaderVariable count = GetSrc(bitwise.count); + + for(uint8_t c = 0; c < var.columns; c++) + { + const uint32_t mask = (1u << count.value.uv[c]) - 1; + + var.value.uv[c] &= ~(mask << offset.value.uv[c]); + var.value.uv[c] |= (insert.value.uv[c] & mask) << offset.value.uv[c]; + } + + SetDst(bitwise.result, var); + break; + } case Op::BitwiseOr: case Op::BitwiseAnd: case Op::BitwiseXor: diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp index b77a78aef..2d0a8a4fe 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp @@ -25,6 +25,7 @@ #include "spirv_debug.h" #include #include "maths/matrix.h" +#include "os/os_specific.h" namespace rdcspv { @@ -585,6 +586,51 @@ ShaderVariable Reflect(ThreadState &state, uint32_t, const rdcarray ¶ms) return N; } +ShaderVariable FindILsb(ThreadState &state, uint32_t, const rdcarray ¶ms) +{ + CHECK_PARAMS(1); + + ShaderVariable x = state.GetSrc(params[0]); + + for(uint8_t c = 0; c < x.columns; c++) + x.value.iv[c] = x.value.uv[c] == 0 ? -1 : Bits::CountTrailingZeroes(x.value.uv[c]); + + return x; +} + +ShaderVariable FindSMsb(ThreadState &state, uint32_t, const rdcarray ¶ms) +{ + CHECK_PARAMS(1); + + ShaderVariable x = state.GetSrc(params[0]); + + for(uint8_t c = 0; c < x.columns; c++) + { + if(x.value.iv[c] == 0 || x.value.iv[c] == -1) + x.value.iv[c] = -1; + else if(x.value.iv[c] >= 0) + x.value.uv[c] = 31 - Bits::CountLeadingZeroes(x.value.uv[c]); + else + x.value.uv[c] = 31 - Bits::CountLeadingZeroes(~x.value.uv[c]); + } + + return x; +} + +ShaderVariable FindUMsb(ThreadState &state, uint32_t, const rdcarray ¶ms) +{ + CHECK_PARAMS(1); + + ShaderVariable x = state.GetSrc(params[0]); + + for(uint8_t c = 0; c < x.columns; c++) + { + x.value.iv[c] = x.value.iv[c] == 0 ? -1 : 31 - Bits::CountLeadingZeroes(x.value.uv[c]); + } + + return x; +} + static float GLSLNMax(float x, float y) { const bool xnan = isnan(x); @@ -711,6 +757,9 @@ void ConfigureGLSLStd450(ExtInstDispatcher &extinst) EXT(Cross); EXT(FaceForward); EXT(Reflect); + EXT(FindILsb); + EXT(FindSMsb); + EXT(FindUMsb); EXT(NMin); EXT(NMax); EXT(NClamp); diff --git a/renderdoc/os/os_specific.cpp b/renderdoc/os/os_specific.cpp index 1af92782b..7f1e631fe 100644 --- a/renderdoc/os/os_specific.cpp +++ b/renderdoc/os/os_specific.cpp @@ -258,6 +258,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 32); + CHECK(Bits::CountTrailingZeroes(val) == 32); + CHECK(Bits::CountOnes(val) == 0); } val = 1; @@ -265,6 +267,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 31); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 1); } val <<= 1; @@ -272,6 +276,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 30); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 1); } val <<= 4; @@ -279,6 +285,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 26); + CHECK(Bits::CountTrailingZeroes(val) == 5); + CHECK(Bits::CountOnes(val) == 1); } val++; @@ -286,6 +294,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 26); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 2); } val += 5; @@ -293,6 +303,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 26); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 3); } val += 1000; @@ -300,6 +312,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 21); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 4); } val *= 3; @@ -307,6 +321,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 20); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 5); } val *= 200000; @@ -314,6 +330,17 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 2); + CHECK(Bits::CountTrailingZeroes(val) == 7); + CHECK(Bits::CountOnes(val) == 12); + } + + val |= 0xFFFFFFFFu; + + { + INFO("val is " << val); + CHECK(Bits::CountLeadingZeroes(val) == 0); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 32); } }; @@ -325,6 +352,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 64); + CHECK(Bits::CountTrailingZeroes(val) == 64); + CHECK(Bits::CountOnes(val) == 0); } val = 1; @@ -332,6 +361,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 63); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 1); } val <<= 1; @@ -339,6 +370,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 62); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 1); } val <<= 4; @@ -346,6 +379,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 58); + CHECK(Bits::CountTrailingZeroes(val) == 5); + CHECK(Bits::CountOnes(val) == 1); } val++; @@ -353,6 +388,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 58); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 2); } val += 5; @@ -360,6 +397,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 58); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 3); } val += 1000; @@ -367,6 +406,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 53); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 4); } val *= 3; @@ -374,6 +415,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 52); + CHECK(Bits::CountTrailingZeroes(val) == 1); + CHECK(Bits::CountOnes(val) == 5); } val *= 200000; @@ -381,6 +424,8 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 34); + CHECK(Bits::CountTrailingZeroes(val) == 7); + CHECK(Bits::CountOnes(val) == 12); } val *= 1000000; @@ -388,6 +433,17 @@ TEST_CASE("Test OS-specific functions", "[osspecific]") { INFO("val is " << val); CHECK(Bits::CountLeadingZeroes(val) == 14); + CHECK(Bits::CountTrailingZeroes(val) == 13); + CHECK(Bits::CountOnes(val) == 19); + } + + val |= 0xFFFFFFFFFFFFFFFFull; + + { + INFO("val is " << val); + CHECK(Bits::CountLeadingZeroes(val) == 0); + CHECK(Bits::CountTrailingZeroes(val) == 0); + CHECK(Bits::CountOnes(val) == 64); } }; #endif diff --git a/renderdoc/os/posix/posix_specific.h b/renderdoc/os/posix/posix_specific.h index c44a456eb..4a3ecff1f 100644 --- a/renderdoc/os/posix/posix_specific.h +++ b/renderdoc/os/posix/posix_specific.h @@ -120,4 +120,28 @@ inline uint64_t CountLeadingZeroes(uint64_t value) return value == 0 ? 64 : __builtin_clzl(value); } #endif + +inline uint32_t CountTrailingZeroes(uint32_t value) +{ + return value == 0 ? 32 : __builtin_ctz(value); +} + +#if ENABLED(RDOC_X64) +inline uint64_t CountTrailingZeroes(uint64_t value) +{ + return value == 0 ? 64 : __builtin_ctzl(value); +} +#endif + +inline uint32_t CountOnes(uint32_t value) +{ + return __builtin_popcount(value); +} + +#if ENABLED(RDOC_X64) +inline uint64_t CountOnes(uint64_t value) +{ + return __builtin_popcountl(value); +} +#endif }; diff --git a/renderdoc/os/win32/win32_specific.h b/renderdoc/os/win32/win32_specific.h index 7aa9e9101..aa2d8595a 100644 --- a/renderdoc/os/win32/win32_specific.h +++ b/renderdoc/os/win32/win32_specific.h @@ -83,4 +83,32 @@ inline uint64_t CountLeadingZeroes(uint64_t value) return (result == TRUE) ? (index ^ 63) : 64; } #endif + +inline uint32_t CountTrailingZeroes(uint32_t value) +{ + DWORD index; + BOOLEAN result = _BitScanForward(&index, value); + return (result == TRUE) ? index : 32; +} + +#if ENABLED(RDOC_X64) +inline uint64_t CountTrailingZeroes(uint64_t value) +{ + DWORD index; + BOOLEAN result = _BitScanForward64(&index, value); + return (result == TRUE) ? index : 64; +} +#endif + +inline uint32_t CountOnes(uint32_t value) +{ + return __popcnt(value); +} + +#if ENABLED(RDOC_X64) +inline uint64_t CountOnes(uint64_t value) +{ + return __popcnt64(value); +} +#endif }; diff --git a/util/test/demos/vk/vk_shader_debug_zoo.cpp b/util/test/demos/vk/vk_shader_debug_zoo.cpp index 8e0b6061c..ea3d1af27 100644 --- a/util/test/demos/vk/vk_shader_debug_zoo.cpp +++ b/util/test/demos/vk/vk_shader_debug_zoo.cpp @@ -847,6 +847,40 @@ void main() } )EOSHADER" R"EOSHADER( + case 102: + { + uint a = zerou + 0x0dadbeef; + int b = zeroi + 0x0dadbeef; + + Color = vec4(float(findLSB(a)), float(findLSB(b)), float(findMSB(a)), float(findMSB(b))); + break; + } + case 103: + { + int a = zeroi - 0x0dadbeef; + + Color = vec4(float(findLSB(a)), float(findLSB(zeroi)), float(findMSB(a)), float(findMSB(zeroi))); + break; + } + case 104: + { + uint a = zerou + 0x44b82a24; + int b = zeroi + 0x44b82a24; + + Color = vec4(float(bitCount(a)), float(bitCount(b)), uintBitsToFloat(bitfieldReverse(a)), intBitsToFloat(bitfieldReverse(b))); + break; + } + case 105: + { + uint a = zerou + 0x44b82a24; + int b = zeroi + 0x44b82a24; + uint af = zerou+0xffffffff; + int bf = zeroi-1; + + Color = vec4(float(bitfieldExtract(a, 4, 5)), float(bitfieldExtract(b, 4, 5)), + uintBitsToFloat(bitfieldInsert(a, af, 4, 5)), intBitsToFloat(bitfieldInsert(b, bf, 4, 5))); + break; + } default: break; } }