From a9a094bcdd93c0155ec4eebb925a7ce1e5b2f9c9 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 18 Oct 2019 18:40:17 +0100 Subject: [PATCH] Add bit reader to decode low-level LLVM bitstream --- .../driver/shaders/dxil/llvm_bitreader.h | 336 ++++++++++ .../driver/shaders/dxil/llvm_decoder.cpp | 631 ++++++++++++++++++ renderdoc/driver/shaders/dxil/llvm_decoder.h | 31 + .../shaders/dxil/renderdoc_dxil.vcxproj | 3 + .../dxil/renderdoc_dxil.vcxproj.filters | 3 + 5 files changed, 1004 insertions(+) create mode 100644 renderdoc/driver/shaders/dxil/llvm_bitreader.h create mode 100644 renderdoc/driver/shaders/dxil/llvm_decoder.cpp create mode 100644 renderdoc/driver/shaders/dxil/llvm_decoder.h diff --git a/renderdoc/driver/shaders/dxil/llvm_bitreader.h b/renderdoc/driver/shaders/dxil/llvm_bitreader.h new file mode 100644 index 000000000..f55d13a23 --- /dev/null +++ b/renderdoc/driver/shaders/dxil/llvm_bitreader.h @@ -0,0 +1,336 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include "common/common.h" + +namespace LLVMBC +{ +class BitReader +{ +public: + BitReader(const byte *bits, size_t length) + : m_Bits(bits), m_Start(bits), m_End(bits + length), m_Offset(0) + { + } + size_t ByteOffset() const { return m_Bits - m_Start; } + size_t BitOffset() const { return ByteOffset() * 8 + m_Offset; } + size_t ByteLength() const { return m_End - m_Start; } + size_t BitLength() const { return (m_End - m_Start) * 8; } + bool AtEndOfStream() const { return m_Bits >= m_End; } + void SeekByte(size_t byteOffset) + { + m_Bits = m_Start + byteOffset; + m_Offset = 0; + } + void SeekBit(size_t bitOffset) + { + m_Bits = m_Start + (bitOffset / 8); + m_Offset = (bitOffset % 8); + } + char c6() + { + byte c = 0; + ReadBits(6, &c); + + if(c >= 0 && c <= 25) + return char('a' + c); + else if(c >= 26 && c <= 51) + return char('A' + c - 26); + else if(c >= 52 && c <= 61) + return char('0' + c - 52); + else if(c == 62) + return '.'; + else if(c == 63) + return '_'; + + RDCERR("Unexpected 6-bit char: %x", (uint32_t)c); + + return '?'; + } + + template + T fixed(const size_t bitWidth) + { + byte scratch[8] = {}; + + RDCASSERT(bitWidth <= 64); + + ReadBits(bitWidth, scratch); + + T ret; + memcpy(&ret, scratch, sizeof(T)); + return ret; + } + + template + T vbr(const size_t groupBitSize) + { + uint64_t ret = 0; + + RDCASSERT(groupBitSize > 1 && "chunk size must be greater than 1"); + RDCASSERT(groupBitSize <= 8 && "Only chunk sizes up to 8 supported"); + byte scratch = 0; + + const byte hibit = 1 << (groupBitSize - 1); + const byte lobits = hibit - 1; + + uint64_t shift = 0; + do + { + ReadBits(groupBitSize, &scratch); + + RDCASSERT(shift <= 63); + + ret += (uint64_t(scratch & lobits) << shift); + + shift += uint64_t(groupBitSize - 1); + } while(scratch & hibit); + + // check for overflow of the return type + const uint64_t mask = ((1ULL << (sizeof(T) * 8 - 1)) - 1) << 1 | 1; + RDCASSERT((ret & mask) == ret); + + return T(ret); + } + + template + T svbr(size_t groupBitSize) + { + // the value will fit in a uint64_t because the any negative values with the high bit set, which + // would overflow when shifted, no longer have the high bit set after being negated. + uint64_t var = vbr(groupBitSize); + + // if the low bit is set, it's negative + if(var & 0x1) + { + return T(-int64_t(var >> 1)); + } + else + { + return T(var >> 1); + } + } + + template + T Read() + { + byte scratch[sizeof(T)] = {}; + + ReadBits(sizeof(T) * 8, scratch); + + T ret; + memcpy(&ret, scratch, sizeof(T)); + return ret; + } + + void ReadBlob(const byte *&blobptr, size_t &bloblen) + { + // get the blob length + bloblen = vbr(6); + + // align to dword boundary + align32bits(); + + // the blob is at m_Bits now + blobptr = m_Bits; + + // advance by the length, and align up as well + m_Bits += bloblen; + align32bits(); + } + + void align32bits() + { + // skip the rest of the current byte, if we're part-way through + if(m_Offset > 0) + Advance(8 - m_Offset); + + const size_t byteOffs = ByteOffset(); + const size_t alignedByteOffs = (byteOffs + 0x3) & ~0x3; + + // advance by N bytes to dword align the stream + m_Bits += (alignedByteOffs - byteOffs); + } + +private: + const byte *m_Bits, *m_Start, *m_End; + size_t m_Offset; + + void Advance(size_t N) + { + m_Offset += N; + // shouldn't read more than this byte + RDCASSERT(m_Offset <= 8); + + // roll over to next byte after consuming all 8 bits + if(m_Offset == 8) + { + m_Bits++; + m_Offset = 0; + } + } + + void ReadBits(size_t bitsToRead, byte *dst) + { + if(BitOffset() + bitsToRead > BitLength()) + { + RDCERR("Reading off end of bitstream"); + + // read 0s off the end of the stream. + // set any whole bytes to 0: + while(bitsToRead >= 8) + { + *dst = 0; + dst++; + bitsToRead -= 8; + } + + // set any remaining bits to 0: + if(bitsToRead) + *dst &= ~((1 << bitsToRead) - 1); + + m_Bits = m_End; + m_Offset = 0; + return; + } + + size_t dstoffs = 0; + + // if we're already partway through a byte, read as many bits as we need and we can + if(m_Offset != 0) + { + const size_t avail = 8 - m_Offset; + + if(avail == bitsToRead) + { + // if we have just enough in this byte, great! shift and mask off, and update the offset + + // grab the bits into the low end + *dst = (*m_Bits >> m_Offset); + + Advance(bitsToRead); + + return; + } + else if(avail > bitsToRead) + { + // we have more than enough. Similar to above but we need to mask out only the bits we need. + + // grab the bits into the low end and mask + *dst = (*m_Bits >> m_Offset) & ((1 << bitsToRead) - 1); + + Advance(bitsToRead); + + return; + } + else + { + // we don't have enough. Consume what we can then continue + *dst = (*m_Bits >> m_Offset); + + dstoffs = avail; + bitsToRead -= avail; + + Advance(avail); + } + } + else + { + // ensure if we didn't read any bits that the byte is zeroed out, so we can OR on bits below + // without needing to worry + *dst = 0; + } + + // we're now at the start of a byte since we read any remainder above. + RDCASSERT(m_Offset == 0); + + // if we have to read whole bytes, do that here. + if(bitsToRead >= 8) + { + if(dstoffs == 0) + { + // if dstoffs is 0 then it's an easy case, we can just copy all the whole bytes into *dst + memcpy(dst, m_Bits, bitsToRead / 8); + + // manual advance + m_Bits += (bitsToRead / 8); + dst += (bitsToRead / 8); + + // make sure we read any sub-byte remainder + bitsToRead &= 0x7; + } + else + { + while(bitsToRead >= 8) + { + // manual advance + const byte cur = *m_Bits; + m_Bits++; + + bitsToRead -= 8; + + // dstoffs doesn't change because we wrap around to the same offset in the next byte. + // However we do need to shuffle the bits in cur around to add what will fit into the + // current byte, and then the remainder into the next byte. + *dst |= (cur << dstoffs); + dst++; + *dst = (cur >> (8 - dstoffs)); + } + } + } + + // if nothing remains, return + if(bitsToRead == 0) + return; + + // we should now have no more than than 7 bits to read + RDCASSERT(bitsToRead < 8); + + // this is the mask to get only the bits we want + const byte mask = ((1 << bitsToRead) - 1); + + // take the bits that we want from the next byte (knowing we want the low-order bits), and shift + // them into where they should go. + byte data = *m_Bits & mask; + + // check if we overlap into the next destination byte + if(dstoffs + bitsToRead < 8) + { + *dst |= data << dstoffs; + } + else + { + *dst |= data << dstoffs; + dst++; + *dst = data >> (8 - dstoffs); + } + + // consume the bits we used + Advance(bitsToRead); + } +}; + +}; // namespace LLVMBC diff --git a/renderdoc/driver/shaders/dxil/llvm_decoder.cpp b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp new file mode 100644 index 000000000..16089c773 --- /dev/null +++ b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp @@ -0,0 +1,631 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "llvm_decoder.h" + +namespace LLVMBC +{ +}; // namespace LLVMBC + +#if ENABLED(ENABLE_UNIT_TESTS) + +#include "3rdparty/catch/catch.hpp" + +TEST_CASE("Check LLVM bitreader", "[llvm]") +{ + SECTION("Check simple reading of bytes") + { + byte bits[] = {0x01, 0x02, 0x40, 0x80, 0xff}; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + + // ensure we can read it all out again in whole bytes + for(size_t i = 0; i < sizeof(bits); i++) + { + byte val = b.Read(); + CHECK(val == bits[i]); + if(i + 1 < sizeof(bits)) + CHECK(!b.AtEndOfStream()); + else + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == i + 1); + CHECK(b.BitOffset() == (i + 1) * 8); + } + } + + SECTION("Check seeking within the stream") + { + byte bits[] = {0x01, 0x4f, 0x8c, 0xff}; + byte val; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + + b.SeekByte(4); + + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == 4); + CHECK(b.BitOffset() == 32); + + b.SeekBit(32); + + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == 4); + CHECK(b.BitOffset() == 32); + + b.SeekBit(29); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 3); + CHECK(b.BitOffset() == 29); + + val = b.fixed(3); + + CHECK(val == 0x7); + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == 4); + CHECK(b.BitOffset() == 32); + + b.SeekBit(0); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + } + + SECTION("Check with empty bitstream") + { + byte bits[] = {0}; + + LLVMBC::BitReader b(bits, 0); + + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + } + + SECTION("Check out of bounds behaviour") + { + byte bits[] = {0x40, 0x80, 0xff}; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + + // first read is fully satisfied, we get the value we expect + uint32_t val1 = b.fixed(17); + CHECK(val1 == 0x18040); + + // second read is partially out of bounds, we should read all 0s + uint32_t val2 = b.fixed(16); + CHECK(val2 == 0); + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Check fixed encoding") + { + // 0x96 = 0b 1001 0110 + // 0xF0 = 0b 1111 0000 + // 0xA5 = 0b 1010 0101 + // 0x3C = 0b 0011 1100 + + // we pad out with 0s so we don't read off the end of the stream when reading up to 4 32-bit + // values + byte bits[] = { + // dword 1 + 0x96, 0xf0, 0xA5, 0x3C, + // padding dword + 0x00, 0x00, 0x00, 0x00, + // padding dword + 0x00, 0x00, 0x00, 0x00, + // padding dword + 0x00, 0x00, 0x00, 0x00, + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + // for each of the bit widths, 1 to 32, read 4 values. + // This should decode from the LSB to MSB in the bitstream - in the commented values above that + // is right-to-left then top-to-bottom + uint32_t expected[32][4] = { + // i_1 + {0x00, 0x01, 0x01, 0x00}, + // i_2 + {0x02, 0x01, 0x01, 0x02}, + // i_3 + {0x06, 0x02, 0x02, 0x00}, + // i_4 + {0x06, 0x09, 0x00, 0x0f}, + // i_5 + {0x16, 0x04, 0x1C, 0x0B}, + // i_6 + {0x16, 0x02, 0x1F, 0x29}, + // i_7 + {0x16, 0x61, 0x17, 0x65}, + // i_8 + {0x96, 0xF0, 0xA5, 0x3C}, + + // i_9 + {0x0096, 0x00F8, 0x0129, 0x0007}, + // i_10 + {0x0096, 0x017C, 0x03CA, 0x0000}, + // i_11 + {0x0096, 0x04BE, 0x00F2, 0x0000}, + // i_12 + {0x0096, 0x0A5F, 0x003C, 0x0000}, + // i_13 + {0x1096, 0x052F, 0x000F, 0x0000}, + // i_14 + {0x3096, 0x3297, 0x0003, 0x0000}, + // i_15 + {0x7096, 0x794B, 0x0000, 0x0000}, + // i_16 + {0xF096, 0x3CA5, 0x0000, 0x0000}, + + // i_17 + {0x0001F096, 0x00001E52}, + // i_18 + {0x0001F096, 0x00000F29}, + // i_19 + {0x0005F096, 0x00000794}, + // i_20 + {0x0005F096, 0x000003CA}, + // i_21 + {0x0005F096, 0x000001E5}, + // i_22 + {0x0025F096, 0x000000F2}, + // i_23 + {0x0025F096, 0x00000079}, + // i_24 + {0x00A5F096, 0x0000003C}, + // i_25 + {0x00A5F096, 0x0000001E}, + // i_26 + {0x00A5F096, 0x0000000F}, + // i_27 + {0x04A5F096, 0x00000007}, + // i_28 + {0x0CA5F096, 0x00000003}, + // i_29 + {0x1CA5F096, 0x00000001}, + // i_30 + {0x3CA5F096, 0x00000000}, + // i_31 + {0x3CA5F096, 0x00000000}, + // i_32 + {0x3CA5F096, 0x00000000}, + }; + + for(size_t i = 0; i < 32; i++) + { + b.SeekBit(0); + uint32_t read; + + INFO("Bit width: " << (i + 1)); + + read = b.fixed(i + 1); + CHECK(read == expected[i][0]); + + read = b.fixed(i + 1); + CHECK(read == expected[i][1]); + + read = b.fixed(i + 1); + CHECK(read == expected[i][2]); + + read = b.fixed(i + 1); + CHECK(read == expected[i][3]); + } + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Check variable encoding") + { + SECTION("Single chunk, no extension") + { + // just set as many bits as we can in one chunk, so all 1s except the MSB + + byte bits[] = { + // i_vbr0 (padding) + 0, + // i_vbr1 (padding) + 0, + // i_vbr2 + 0x01, + // i_vbr3 + 0x03, + // i_vbr4 + 0x07, + // i_vbr5 + 0x0f, + // i_vbr6 + 0x1f, + // i_vbr7 + 0x3f, + // i_vbr8 + 0x7f, + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + for(size_t i = 2; i <= 8; i++) + { + INFO("VBR group size: " << i); + b.SeekByte(i); + + uint64_t val = b.vbr(i); + CHECK(val == bits[i]); + } + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Two chunks, one extension") + { + // set all bits that we can from two chunks - that means the first chunk is all 1s, the second + // is all 1s except the leading 0 + + byte bits[] = { + // i_vbr0 (padding) + 0, 0, + // i_vbr1 (padding) + 0, 0, + // i_vbr2 + 0x07, 0x00, // 0b 01 11 + // i_vbr3 + 0x1f, 0x00, // 0b 011 111 + // i_vbr4 + 0x7f, 0x00, // 0b 0111 1111 + // i_vbr5 + 0xff, 0x01, // 0b 01111 11111 + // i_vbr6 + 0xff, 0x07, // 0b 011111 111111 + // i_vbr7 + 0xff, 0x1f, // 0b 0111111 1111111 + // i_vbr8 + 0xff, 0x7f, // 0b 01111111 11111111 + }; + + uint64_t expected[] = { + 0, 0, + // i_vbr2 + 0x0003, + // i_vbr3 + 0x000f, + // i_vbr4 + 0x003f, + // i_vbr5 + 0x00ff, + // i_vbr6 + 0x03ff, + // i_vbr7 + 0x0fff, + // i_vbr8 + 0x3fff, + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + for(size_t i = 2; i <= 8; i++) + { + INFO("VBR group size: " << i); + b.SeekByte(i * 2); + + uint64_t val = b.vbr(i); + CHECK(val == expected[i]); + } + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Five chunks, four extensions") + { + // set an alternating 10 pattern from the top bit. Each group except the last has a leading 1 + + byte bits[] = { + // i_vbr0 (padding) + 0, 0, 0, 0, 0, + // i_vbr1 (padding) + 0, 0, 0, 0, 0, + // i_vbr2 + 0xBB, 0x01, 0x00, 0x00, 0x00, // 0b 01 10 11 10 11 + // i_vbr3 + 0xB6, 0x2D, 0x00, 0x00, 0x00, // 0b 010 110 110 110 110 + // i_vbr4 + 0xAD, 0xAD, 0x05, 0x00, 0x00, // 0b 0101 1010 1101 1010 1101 + // i_vbr5 + 0x5A, 0x6B, 0xAD, 0x00, 0x00, // 0b 01010 11010 11010 11010 11010 + // i_vbr6 + 0xB5, 0x5A, 0xAB, 0x15, 0x00, // 0b 010101 101010 110101 101010 110101 + // i_vbr7 + 0x6A, 0xB5, 0x5A, 0xAD, 0x02, // 0b 0101010 1101010 1101010 1101010 1101010 + // i_vbr8 + 0xD5, 0xAA, 0xD5, 0xAA, 0x55, // 0b 01010101 10101010 11010101 10101010 11010101 + }; + + uint64_t expected[] = { + 0, 0, + // i_vbr2 + 0x0000000015ULL, // 0b 1 0 1 0 1 + // i_vbr3 + 0x00000002AAULL, // 0b 10 10 10 10 10 + // i_vbr4 + 0x0000005555ULL, // 0b 101 010 101 010 101 + // i_vbr5 + 0x00000AAAAAULL, // 0b 1010 1010 1010 1010 1010 + // i_vbr6 + 0x0001555555ULL, // 0b 10101 01010 10101 01010 10101 + // i_vbr7 + 0x002AAAAAAAULL, // 0b 101010 101010 101010 101010 101010 + // i_vbr8 + 0x0555555555ULL, // 0b 1010101 0101010 1010101 0101010 1010101 + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + for(size_t i = 2; i <= 8; i++) + { + INFO("VBR group size: " << i); + b.SeekByte(i * 5); + + uint64_t val = b.vbr(i); + CHECK(val == expected[i]); + } + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Check signed vbr decoding") + { + // we don't check every possible bit width since this is decoded the same as vbr except for a + // post-check and shift. Instead we use vbr4 since it's convenient for hex literals + byte bits[] = { + 0x04, // 0b 0100 = +2 + 0x05, // 0b 0101 = -2 + 0xBA, 0x9E, 0x68, // 0b 0110 1000 1001 1110 1011 1010 = +98765 + 0xBB, 0x9E, 0x68, // 0b 0110 1000 1001 1110 1011 1011 = -98765 + // INT64_MAX. 64-bits encoded in 3-bit groups is 22 groups, so 22 * 4-bit encoded groups + // is 88 bits, meaning 11 bytes + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + // INT64_MIN. Same as above but with the LSB set to 1 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + // one more value just to check that we didn't overrun above + 0x06, // 0b 0110 = +3 + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + int64_t val; + + val = b.svbr(4); + CHECK(val == 2); + + val = b.svbr(4); + CHECK(val == 0); + + val = b.svbr(4); + CHECK(val == -2); + + val = b.svbr(4); + CHECK(val == 0); + + val = b.svbr(4); + CHECK(val == 98765); + + val = b.svbr(4); + CHECK(val == -98765); + + val = b.svbr(4); + CHECK(val == INT64_MAX); + + val = b.svbr(4); + CHECK(val == -INT64_MAX); + + val = b.svbr(4); + CHECK(val == 3); + + val = b.svbr(4); + CHECK(val == 0); + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + } + + SECTION("Check char6 encoding") + { + byte bits[64] = {}; + for(size_t i = 0; i < sizeof(bits); i++) + bits[i] = i & 0xff; + + // this is the char6 encoding + const char string[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._"; + + RDCCOMPILE_ASSERT(sizeof(string) - 1 == sizeof(bits), + "bits byte array and string should be same size."); + + LLVMBC::BitReader b(bits, sizeof(bits)); + + for(size_t i = 0; i < sizeof(bits); i++) + { + char c = b.c6(); + // for simplicity we read padding too + byte pad = b.fixed(2); + + CHECK(c == string[i]); + CHECK(pad == 0); + } + } + + SECTION("Check 32-bit aligning") + { + byte bits[] = { + // first i_4 value + 0x04, + // padding for alignment + 0x00, 0x00, 0x00, + + // second two i_4 values + 0xF5, + // i_24 value + 0xCA, 0x99, 0x23, + + // no padding - already aligned + + // i_6 value and i_2 value + 0xBF, + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + + uint32_t val; + + // first read is fully satisfied, we get the value we expect + val = b.fixed(4); + CHECK(val == 0x4); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 4); + + b.align32bits(); + + CHECK(b.ByteOffset() == 4); + CHECK(b.BitOffset() == 32); + + val = b.fixed(4); + CHECK(val == 0x5); + + val = b.fixed(4); + CHECK(val == 0xf); + + val = b.fixed(24); + CHECK(val == 0x2399CA); + + CHECK(b.ByteOffset() == 8); + CHECK(b.BitOffset() == 64); + + // should be a no-op because we're already aligned + b.align32bits(); + + CHECK(b.ByteOffset() == 8); + CHECK(b.BitOffset() == 64); + + val = b.fixed(6); + CHECK(val == 0x3f); + + val = b.fixed(2); + CHECK(val == 0x2); + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } + + SECTION("Check blob fetch") + { + // size = 16 bytes for encoded data and first blob, 70 bytes for second blob, 2 bytes trailing + // padding + byte bits[16 + 70 + 2] = { + // first vbr_6 length + 0x06, + // padding for alignment + 0x00, 0x00, 0x00, + + // blob data + 0xF5, 0x00, 0xCA, 0x40, 0x99, 0x23, + + // padding for trailing alignment + 0x00, 0x00, + + // i_20 dummy to get us to the point where two vbr_6 chunks would be aligned + // we choose a length of 70, which is 0b10 00110, then vbr_6 encoded it becomes + // 0b000010 100110 which is 0xA6, over 12 bits. That leaves 4 bits in the upper part of + // the last byte of the i_20, and the remaining 8 in the next byte + 0x5B, 0xC2, 0x64, 0x0A, + }; + + LLVMBC::BitReader b(bits, sizeof(bits)); + + CHECK(!b.AtEndOfStream()); + CHECK(b.ByteOffset() == 0); + CHECK(b.BitOffset() == 0); + + const byte *ptr = NULL; + size_t size = 0; + + b.ReadBlob(ptr, size); + + CHECK(size == 6); + CHECK(ptr == &bits[4]); + + uint32_t val = b.fixed(20); + CHECK(val == 0x4C25B); + + ptr = NULL; + size = 0; + + b.ReadBlob(ptr, size); + + CHECK(size == 70); + CHECK(ptr == &bits[16]); + + // should be exactly at the end of the stream + CHECK(b.AtEndOfStream()); + CHECK(b.ByteOffset() == sizeof(bits)); + CHECK(b.BitOffset() == sizeof(bits) * 8); + } +} + +#endif \ No newline at end of file diff --git a/renderdoc/driver/shaders/dxil/llvm_decoder.h b/renderdoc/driver/shaders/dxil/llvm_decoder.h new file mode 100644 index 000000000..47bee4b29 --- /dev/null +++ b/renderdoc/driver/shaders/dxil/llvm_decoder.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include "llvm_bitreader.h" + +namespace LLVMBC +{ +}; // namespace LLVMBC diff --git a/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj b/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj index f90fa8852..c04c9e0c0 100644 --- a/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj +++ b/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj @@ -100,12 +100,15 @@ + Create + + diff --git a/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj.filters b/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj.filters index 63ebfbc6d..f2a87609f 100644 --- a/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj.filters +++ b/renderdoc/driver/shaders/dxil/renderdoc_dxil.vcxproj.filters @@ -5,12 +5,15 @@ PCH + PCH + +