From 81b4d3d804c1974b0b442417780c4e2b7a8f0fa8 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 7 Jul 2021 12:12:32 +0100 Subject: [PATCH] Add public domain md5 library and implement modified DXBC container hash * This allows us to disabled any requirement for "experimental shaders". --- docs/credits_acknowledgements.rst | 4 + renderdoc/3rdparty/md5/README.md | 38 ++ renderdoc/3rdparty/md5/md5.c | 291 ++++++++++++++ renderdoc/3rdparty/md5/md5.h | 54 +++ renderdoc/CMakeLists.txt | 2 + renderdoc/driver/d3d12/d3d12_replay.cpp | 21 - renderdoc/driver/d3d12/d3d12_shader_cache.cpp | 4 + .../driver/shaders/dxbc/dxbc_container.cpp | 363 +++++++++++++++++- .../driver/shaders/dxbc/dxbc_container.h | 3 + renderdoc/renderdoc.vcxproj | 9 + renderdoc/renderdoc.vcxproj.filters | 9 + util/installer/LICENSE.rtf | 1 + util/test/demos/3rdparty/md5/README.md | 38 ++ util/test/demos/3rdparty/md5/md5.c | 291 ++++++++++++++ util/test/demos/3rdparty/md5/md5.h | 54 +++ util/test/demos/d3d12/d3d12_test.cpp | 133 ++++++- util/test/demos/d3d12/d3d12_test.h | 3 + util/test/demos/demos.vcxproj | 2 + util/test/demos/demos.vcxproj.filters | 9 + 19 files changed, 1299 insertions(+), 30 deletions(-) create mode 100644 renderdoc/3rdparty/md5/README.md create mode 100644 renderdoc/3rdparty/md5/md5.c create mode 100644 renderdoc/3rdparty/md5/md5.h create mode 100644 util/test/demos/3rdparty/md5/README.md create mode 100644 util/test/demos/3rdparty/md5/md5.c create mode 100644 util/test/demos/3rdparty/md5/md5.h diff --git a/docs/credits_acknowledgements.rst b/docs/credits_acknowledgements.rst index f23578f2a..8341fbc27 100644 --- a/docs/credits_acknowledgements.rst +++ b/docs/credits_acknowledgements.rst @@ -128,6 +128,10 @@ The following libraries and components are incorporated into RenderDoc, listed h Used to intercept and test AGS calls on D3D11/D3D12. +* `md5 hashing `_ - Released to the Public Domain by Alexander Peslyak. + + Used for calculating DXBC container hash. + Thanks ------ diff --git a/renderdoc/3rdparty/md5/README.md b/renderdoc/3rdparty/md5/README.md new file mode 100644 index 000000000..e424b3ec1 --- /dev/null +++ b/renderdoc/3rdparty/md5/README.md @@ -0,0 +1,38 @@ +Fetched from https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 on 2021-07-07 + +Public domain licensed: + +> This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. +> MD5 Message-Digest Algorithm (RFC 1321). +> +> Homepage: +> http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 +> +> Author: +> Alexander Peslyak, better known as Solar Designer +> +> This software was written by Alexander Peslyak in 2001. No copyright is +> claimed, and the software is hereby placed in the public domain. +> In case this attempt to disclaim copyright and place the software in the +> public domain is deemed null and void, then the software is +> Copyright (c) 2001 Alexander Peslyak and it is hereby released to the +> general public under the following terms: +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted. +> +> There's ABSOLUTELY NO WARRANTY, express or implied. +> +> (This is a heavily cut-down "BSD license".) +> +> This differs from Colin Plumb's older public domain implementation in that +> no exactly 32-bit integer data type is required (any 32-bit or wider +> unsigned integer data type will do), there's no compile-time endianness +> configuration, and the function prototypes match OpenSSL's. No code from +> Colin Plumb's implementation has been reused; this comment merely compares +> the properties of the two independent implementations. +> +> The primary goals of this implementation are portability and ease of use. +> It is meant to be fast, but not as fast as possible. Some known +> optimizations are not included to reduce source code size and avoid +> compile-time configuration. diff --git a/renderdoc/3rdparty/md5/md5.c b/renderdoc/3rdparty/md5/md5.c new file mode 100644 index 000000000..b235e17a5 --- /dev/null +++ b/renderdoc/3rdparty/md5/md5.c @@ -0,0 +1,291 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/renderdoc/3rdparty/md5/md5.h b/renderdoc/3rdparty/md5/md5.h new file mode 100644 index 000000000..d9dac8f41 --- /dev/null +++ b/renderdoc/3rdparty/md5/md5.h @@ -0,0 +1,54 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Added by baldurk, for C++ compatibility */ +#if defined(__cplusplus) +extern "C" { +#endif + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#if defined(__cplusplus) +}; // extern "C" +#endif + +#endif diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index 1ef002f69..0427ece07 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -214,6 +214,8 @@ set(sources 3rdparty/pugixml/pugiconfig.hpp 3rdparty/lz4/lz4.c 3rdparty/lz4/lz4.h + 3rdparty/md5/md5.c + 3rdparty/md5/md5.h 3rdparty/miniz/miniz.c 3rdparty/miniz/miniz.h 3rdparty/superluminal/superluminal.cpp diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index af39528e8..4b129cfcc 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -4026,27 +4026,6 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I D3D12_PrepareReplaySDKVersion(initParams.SDKVersion, D3D12Core, D3D12SDKLayers, D3D12Lib); - if(rdc) - { - using PFN_ENABLE_EXPERIMENTAL = decltype(&D3D12EnableExperimentalFeatures); - - PFN_ENABLE_EXPERIMENTAL EnableExperimental = - (PFN_ENABLE_EXPERIMENTAL)GetProcAddress(D3D12Lib, "D3D12EnableExperimentalFeatures"); - - if(EnableExperimental) - { - HRESULT hr = EnableExperimental(1, &D3D12ExperimentalShaderModels, NULL, NULL); - if(SUCCEEDED(hr)) - RDCLOG("Enabled experimental shaders"); - else - RDCLOG("Couldn't enable experimental shaders"); - } - else - { - RDCLOG("Couldn't get D3D12EnableExperimentalFeatures"); - } - } - const bool isProxy = (rdc == NULL); AMDRGPControl *rgp = NULL; diff --git a/renderdoc/driver/d3d12/d3d12_shader_cache.cpp b/renderdoc/driver/d3d12/d3d12_shader_cache.cpp index c7262522a..cd8a534a0 100644 --- a/renderdoc/driver/d3d12/d3d12_shader_cache.cpp +++ b/renderdoc/driver/d3d12/d3d12_shader_cache.cpp @@ -424,6 +424,10 @@ rdcstr D3D12ShaderCache::GetShaderBlob(const char *source, const char *entry, D3D12ShaderCacheCallbacks.Create((uint32_t)code->GetBufferSize(), code->GetBufferPointer(), &byteBlob); + if(!DXBC::DXBCContainer::IsHashedContainer(byteBlob->GetBufferPointer(), + byteBlob->GetBufferSize())) + DXBC::DXBCContainer::HashContainer(byteBlob->GetBufferPointer(), byteBlob->GetBufferSize()); + SAFE_RELEASE(code); } else diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index 3f80b5767..1c175fc17 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -30,6 +30,7 @@ #include "core/settings.h" #include "driver/shaders/dxil/dxil_bytecode.h" #include "lz4/lz4.h" +#include "md5/md5.h" #include "serialise/serialiser.h" #include "strings/string_utils.h" #include "dxbc_bytecode.h" @@ -104,7 +105,7 @@ struct FileHeader { uint32_t fourcc; // "DXBC" uint32_t hashValue[4]; // unknown hash function and data - uint32_t unknown; + uint32_t containerVersion; uint32_t fileLength; uint32_t numChunks; // uint32 chunkOffsets[numChunks]; follows @@ -691,6 +692,125 @@ void DXBCContainer::GetHash(uint32_t hash[4], const void *ByteCode, size_t Bytec } } +bool DXBCContainer::IsHashedContainer(void *ByteCode, size_t BytecodeLength) +{ + if(BytecodeLength < sizeof(FileHeader)) + return false; + + FileHeader *header = (FileHeader *)ByteCode; + + if(header->fourcc != FOURCC_DXBC) + return false; + + if(header->fileLength != (uint32_t)BytecodeLength) + return false; + + if(header->hashValue[0] != 0 || header->hashValue[1] != 0 || header->hashValue[2] != 0 || + header->hashValue[3] != 0) + return true; + + return false; +} + +bool DXBCContainer::HashContainer(void *ByteCode, size_t BytecodeLength) +{ + if(BytecodeLength < sizeof(FileHeader)) + return false; + + FileHeader *header = (FileHeader *)ByteCode; + + if(header->fourcc != FOURCC_DXBC) + return false; + + if(header->fileLength != (uint32_t)BytecodeLength) + return false; + + MD5_CTX md5ctx = {}; + MD5_Init(&md5ctx); + + // the hashable data starts immediately after the hash. + byte *data = (byte *)&header->containerVersion; + uint32_t length = uint32_t(BytecodeLength - offsetof(FileHeader, containerVersion)); + + // we need to know the number of bits for putting in the trailing padding. + uint32_t numBits = length * 8; + uint32_t numBitsPart2 = (numBits >> 2) | 1; + + // MD5 works on 64-byte chunks, process the first set of whole chunks, leaving 0-63 bytes left + // over + uint32_t leftoverLength = length % 64; + MD5_Update(&md5ctx, data, length - leftoverLength); + + data += length - leftoverLength; + + uint32_t block[16] = {}; + RDCCOMPILE_ASSERT(sizeof(block) == 64, "Block is not properly sized for MD5 round"); + + // normally MD5 finishes by appending a 1 bit to the bitstring. Since we are only appending bytes + // this would be an 0x80 byte (the first bit is considered to be the MSB). Then it pads out with + // zeroes until it has 56 bytes in the last block and appends appends the message length as a + // 64-bit integer as the final part of that block. + // in other words, normally whatever is leftover from the actual message gets one byte appended, + // then if there's at least 8 bytes left we'll append the length. Otherwise we pad that block with + // 0s and create a new block with the length at the end. + // Or as the original RFC/spec says: padding is always performed regardless of whether the + // original buffer already ended in exactly a 56 byte block. + // + // The DXBC finalisation is slightly different (previous work suggests this is due to a bug in the + // original implementation and it was maybe intended to be exactly MD5?): + // + // The length provided in the padding block is not 64-bit properly: the second dword with the high + // bits is instead the number of nybbles(?) with 1 OR'd on. The length is also split, so if it's + // in + // a padding block the low bits are in the first dword and the upper bits in the last. If there's + // no padding block the low dword is passed in first before the leftovers of the message and then + // the upper bits at the end. + + // if the leftovers uses at least 56, we can't fit both the trailing 1 and the 64-bit length, so + // we need a padding block and then our own block for the length. + if(leftoverLength >= 56) + { + // pass in the leftover data padded out to 64 bytes with zeroes + MD5_Update(&md5ctx, data, leftoverLength); + + block[0] = 0x80; // first padding bit is 1 + MD5_Update(&md5ctx, block, 64 - leftoverLength); + + // the final block contains the number of bits in the first dword, and the weird upper bits + block[0] = numBits; + block[15] = numBitsPart2; + + // process this block directly, we're replacing the call to MD5_Final here manually + MD5_Update(&md5ctx, block, 64); + } + else + { + // the leftovers mean we can put the padding inside the final block. But first we pass the "low" + // number of bits: + MD5_Update(&md5ctx, &numBits, sizeof(numBits)); + + if(leftoverLength) + MD5_Update(&md5ctx, data, leftoverLength); + + uint32_t paddingBytes = 64 - leftoverLength - 4; + + // prepare the remainder of this block, starting with the 0x80 padding start right after the + // leftovers and the first part of the bit length above. + block[0] = 0x80; + // then add the remainder of the 'length' here in the final part of the block + memcpy(((byte *)block) + paddingBytes - 4, &numBitsPart2, 4); + + MD5_Update(&md5ctx, block, paddingBytes); + } + + header->hashValue[0] = md5ctx.a; + header->hashValue[1] = md5ctx.b; + header->hashValue[2] = md5ctx.c; + header->hashValue[3] = md5ctx.d; + + return true; +} + bool DXBCContainer::UsesExtensionUAV(uint32_t slot, uint32_t space, const void *ByteCode, size_t BytecodeLength) { @@ -2081,6 +2201,247 @@ TEST_CASE("DO NOT COMMIT - convenience test", "[dxbc]") #endif +#include "dxbc_compile.h" + +TEST_CASE("Check DXBC hash algorithm", "[dxbc]") +{ + SECTION("Test live compiles against fxc") + { + HMODULE d3dcompiler = GetD3DCompiler(); + + if(!d3dcompiler) + return; + + pD3DCompile compileFunc = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile"); + + if(compileFunc == NULL) + { + RDCFATAL("Can't get D3DCompile from d3dcompiler_??.dll"); + } + + HRESULT hr = S_OK; + + ID3DBlob *byteBlob = NULL; + + // don't include debug info + uint32_t flags = D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; + + // create snippets that affect the compilation since we don't have embedded source + rdcarray snippets = { + R"( +)", + R"( +ret.x = sin(ret.x); +)", + R"( +ret.xy = cos(ret.zw * ret.xy); +)", + R"( +ret.xy += sqrt(ret.z).xx; +)", + R"( +ret.zw += tex.Load(ret.xyz).yz; +)", + }; + + // add 128 snippets, each with a one character long input to slowly inflate the reflection + // data. + // The intent is to produce bytecodes of length 0 through 60 mod 64 (since bytecode is always + // dword aligned) + { + const char *snippet = R"( +#define TEX_NAME tex%s +Texture2D TEX_NAME : register(t0); +float4 main(float3 input : INPUT) : SV_Target0 +{ + return TEX_NAME.Load(input); +} +)"; + rdcstr extra; + for(int i = 0; i < 128; i++) + { + snippets.push_back(StringFormat::Fmt(snippet, extra.c_str())); + extra += 'A'; + } + } + + bool dwordLength[15] = {}; + + for(rdcstr snippet : snippets) + { + rdcstr source; + + if(snippet.contains("main(")) + source = snippet; + else + source = R"( +Texture2D tex : register(t0); + +float4 main(float input : INPUT) : SV_Target0 +{ + float4 ret = input.xxxx; +)" + snippet + + R"( + return ret; +} + +)"; + + ID3DBlob *errBlob; + hr = compileFunc(source.c_str(), source.size(), "main", NULL, NULL, "main", "ps_5_0", flags, + 0, &byteBlob, &errBlob); + + if(errBlob) + RDCLOG("%s", (char *)errBlob->GetBufferPointer()); + + REQUIRE(SUCCEEDED(hr)); + if(SUCCEEDED(hr)) + { + bytebuf bytecode; + bytecode.assign((const byte *)byteBlob->GetBufferPointer(), byteBlob->GetBufferSize()); + + REQUIRE(bytecode.size() % 4 == 0); + dwordLength[(bytecode.size() % 64) / 4] = true; + + bytebuf hashed = bytecode; + DXBC::FileHeader *header = (DXBC::FileHeader *)hashed.data(); + RDCEraseEl(header->hashValue); + + DXBC::DXBCContainer::HashContainer(hashed.data(), hashed.size()); + + bool same = (bytecode == hashed); + CHECK(same); + } + + SAFE_RELEASE(byteBlob); + } + + // check that we've tested every length, mod 64. + for(int i = 0; i < ARRAY_COUNT(dwordLength); i++) + CHECK(dwordLength[i]); + } + + SECTION("Test odd-sized buffer") + { + // dxc produces non-dword sized containers, but we don't want to pull dxc into our tests so we + // instead test a fixed known shader + + bytebuf dxil = { + 0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xef, 0x05, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, 0x53, 0x46, 0x49, 0x30, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, + 0x31, 0x2f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x4e, 0x50, 0x55, 0x54, 0x41, 0x00, 0x4f, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x50, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x50, 0x53, 0x56, 0x30, 0x74, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x41, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x44, 0x03, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x28, 0x08, 0x8c, 0xa0, 0xf5, 0x45, + 0x32, 0x63, 0x6a, 0x19, 0x1b, 0xa0, 0xf6, 0xc4, 0x76, 0x44, 0x58, 0x49, 0x4c, 0x94, 0x04, + 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x25, 0x01, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x00, + 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x00, 0x00, 0x42, 0x43, 0xc0, 0xde, + 0x21, 0x0c, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, + 0x32, 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, 0x80, + 0x10, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0x84, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, 0x4b, + 0x0a, 0x32, 0x42, 0x88, 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, + 0x42, 0xe4, 0x48, 0x0e, 0x90, 0x11, 0x22, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, + 0xe5, 0x8a, 0x04, 0x21, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1b, + 0x88, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x40, 0x02, 0x00, 0x00, 0x49, 0x18, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x13, 0x82, 0x00, 0x00, 0x89, 0x20, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x32, 0x22, 0x08, 0x09, 0x20, 0x64, 0x85, 0x04, 0x13, 0x22, 0xa4, 0x84, 0x04, 0x13, + 0x22, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x88, 0x8c, 0x0b, 0x84, 0x84, 0x4c, 0x10, + 0x28, 0x23, 0x00, 0x25, 0x00, 0x8a, 0x39, 0x02, 0x30, 0x98, 0x23, 0x40, 0x66, 0x00, 0x8a, + 0x01, 0x33, 0x43, 0x45, 0x36, 0x10, 0x90, 0x03, 0x03, 0x00, 0x00, 0x00, 0x13, 0x14, 0x72, + 0xc0, 0x87, 0x74, 0x60, 0x87, 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, + 0xaf, 0x50, 0x0e, 0x6d, 0xd0, 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, 0x07, + 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, 0x07, + 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, + 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, + 0x6d, 0x90, 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, + 0x76, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, 0x20, 0x07, 0x7a, 0x30, 0x07, + 0x72, 0xd0, 0x06, 0xe6, 0x60, 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x6d, 0xe0, 0x0e, + 0x78, 0xa0, 0x07, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x76, 0x40, 0x07, + 0x43, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, + 0x06, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x81, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x10, 0x19, 0x11, 0x4c, 0x90, 0x8c, 0x09, 0x26, + 0x47, 0xc6, 0x04, 0x43, 0x9a, 0x12, 0x18, 0x01, 0x28, 0x85, 0x62, 0x28, 0x83, 0xf2, 0x20, + 0x2a, 0x89, 0x11, 0x80, 0x12, 0x28, 0x83, 0x42, 0xa0, 0x1c, 0x6b, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x4c, 0x90, + 0x46, 0x02, 0x13, 0x44, 0x35, 0x18, 0x63, 0x0b, 0x73, 0x3b, 0x03, 0xb1, 0x2b, 0x93, 0x9b, + 0x4b, 0x7b, 0x73, 0x03, 0x99, 0x71, 0xb9, 0x01, 0x41, 0xa1, 0x0b, 0x3b, 0x9b, 0x7b, 0x91, + 0x2a, 0x62, 0x2a, 0x0a, 0x9a, 0x2a, 0xfa, 0x9a, 0xb9, 0x81, 0x79, 0x31, 0x4b, 0x73, 0x0b, + 0x63, 0x4b, 0xd9, 0x10, 0x04, 0x13, 0x84, 0x41, 0x98, 0x20, 0x0c, 0xc3, 0x06, 0x61, 0x20, + 0x26, 0x08, 0x03, 0xb1, 0x41, 0x18, 0x0c, 0x0a, 0x76, 0x73, 0x13, 0x84, 0xa1, 0xd8, 0x30, + 0x20, 0x09, 0x31, 0x41, 0x48, 0x9a, 0x0d, 0xc1, 0x32, 0x41, 0x10, 0x00, 0x12, 0x6d, 0x61, + 0x69, 0x6e, 0x34, 0x92, 0x9c, 0xa0, 0xaa, 0xa8, 0x82, 0x26, 0x08, 0x04, 0x32, 0x41, 0x20, + 0x92, 0x0d, 0x01, 0x31, 0x41, 0x20, 0x94, 0x0d, 0x0b, 0xf1, 0x40, 0x91, 0x14, 0x0d, 0x13, + 0x11, 0x01, 0x1b, 0x02, 0x8a, 0xcb, 0x94, 0xd5, 0x17, 0xd4, 0xdb, 0x5c, 0x1a, 0x5d, 0xda, + 0x9b, 0xdb, 0x04, 0x81, 0x58, 0x26, 0x08, 0x04, 0x33, 0x41, 0x18, 0x8c, 0x09, 0xc2, 0x70, + 0x6c, 0x10, 0x32, 0x6d, 0xc3, 0x42, 0x58, 0xd0, 0x25, 0x61, 0x03, 0x46, 0x44, 0xdb, 0x86, + 0x80, 0xdb, 0x30, 0x54, 0x1d, 0xb0, 0xa1, 0x68, 0x1c, 0x0f, 0x00, 0xaa, 0xb0, 0xb1, 0xd9, + 0xb5, 0xb9, 0xa4, 0x91, 0x95, 0xb9, 0xd1, 0x4d, 0x09, 0x82, 0x2a, 0x64, 0x78, 0x2e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x02, 0xa2, 0x09, 0x19, 0x9e, 0x8b, 0x5d, 0x18, + 0x9b, 0x5d, 0x99, 0xdc, 0x94, 0xc0, 0xa8, 0x43, 0x86, 0xe7, 0x32, 0x87, 0x16, 0x46, 0x56, + 0x26, 0xd7, 0xf4, 0x46, 0x56, 0xc6, 0x36, 0x25, 0x48, 0xea, 0x90, 0xe1, 0xb9, 0xd8, 0xa5, + 0x95, 0xdd, 0x25, 0x91, 0x4d, 0xd1, 0x85, 0xd1, 0x95, 0x4d, 0x09, 0x96, 0x3a, 0x64, 0x78, + 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x53, 0x02, 0x0f, + 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, + 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, + 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, 0x00, 0x0f, 0xed, 0x10, 0x0e, 0xf4, 0x80, + 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, + 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, + 0xcc, 0x78, 0x8c, 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, 0x87, 0x70, 0x70, 0x07, + 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, 0x11, 0x0e, 0xec, 0x90, + 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, 0x0e, 0x33, 0x10, + 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, 0x3b, + 0xbc, 0x83, 0x3b, 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, 0x3b, + 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, + 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, + 0x76, 0x78, 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, + 0x79, 0x98, 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, + 0x03, 0x62, 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xdc, + 0x61, 0x1c, 0xca, 0x21, 0x1c, 0xc4, 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, + 0xc8, 0x43, 0x39, 0x98, 0x43, 0x39, 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, 0x38, + 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, 0x3b, + 0xb0, 0xc3, 0x0c, 0xc4, 0x21, 0x07, 0x7c, 0x70, 0x03, 0x7a, 0x28, 0x87, 0x76, 0x80, 0x87, + 0x19, 0xd1, 0x43, 0x0e, 0xf8, 0xe0, 0x06, 0xe4, 0x20, 0x0e, 0xe7, 0xe0, 0x06, 0xf6, 0x10, + 0x0e, 0xf2, 0xc0, 0x0e, 0xe1, 0x90, 0x0f, 0xef, 0x50, 0x0f, 0xf4, 0x00, 0x00, 0x00, 0x71, + 0x20, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x50, 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x34, + 0x39, 0x11, 0x81, 0x52, 0xd3, 0x43, 0x4d, 0x7e, 0x71, 0xdb, 0x06, 0x40, 0x30, 0x00, 0xd2, + 0x00, 0x61, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x13, 0x04, 0x41, 0x2c, 0x10, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x44, 0x45, 0x40, 0x35, 0x46, 0x00, 0x82, 0x20, 0x88, + 0x7f, 0x63, 0x04, 0x20, 0x08, 0x82, 0x20, 0x18, 0x8c, 0x11, 0x80, 0x20, 0x08, 0x92, 0x60, + 0x30, 0x46, 0x00, 0x82, 0x20, 0x88, 0x82, 0x01, 0x00, 0x00, 0x00, 0x00, 0x23, 0x06, 0x09, + 0x00, 0x82, 0x60, 0x60, 0x48, 0x0f, 0x04, 0x29, 0xc4, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, + 0x18, 0xd2, 0x03, 0x41, 0xc9, 0x30, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0x86, 0xf4, 0x40, + 0x50, 0x21, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x81, 0x21, 0x3d, 0x10, 0x84, 0x04, 0x08, + 0x00, 0x00, 0x00, 0x00, + }; + + DXBC::DXBCContainer::HashContainer(dxil.data(), dxil.size()); + + DXBC::FileHeader *header = (DXBC::FileHeader *)dxil.data(); + + CHECK(header->hashValue[0] == 3739765114); + CHECK(header->hashValue[1] == 3689508432); + CHECK(header->hashValue[2] == 2832704775); + CHECK(header->hashValue[3] == 3632933760); + } +} + TEST_CASE("Check DXBC flags are non-overlapping", "[dxbc]") { for(const DXBC::FxcArg &a : DXBC::fxc_flags) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.h b/renderdoc/driver/shaders/dxbc/dxbc_container.h index 6cabf8eaf..ec436949e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.h @@ -167,6 +167,9 @@ public: const DXIL::Program *GetDXILByteCode() { return m_DXILByteCode; } static void GetHash(uint32_t hash[4], const void *ByteCode, size_t BytecodeLength); + static bool IsHashedContainer(void *ByteCode, size_t BytecodeLength); + static bool HashContainer(void *ByteCode, size_t BytecodeLength); + static bool UsesExtensionUAV(uint32_t slot, uint32_t space, const void *ByteCode, size_t BytecodeLength); diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index e682588e6..8542f3c74 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -135,6 +135,7 @@ + @@ -314,6 +315,14 @@ true true + + NotUsing + + + MaxSpeed + true + true + 4100;4127 NotUsing diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 237c5f04e..d05fa6625 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -148,6 +148,9 @@ {1b49d2aa-1cfb-4f0a-8fcf-1065af55ea2b} + + {fcbf71ce-3767-435c-9aa2-e1327b9016f4} + @@ -564,6 +567,9 @@ Core + + 3rdparty\md5 + @@ -977,6 +983,9 @@ Core + + 3rdparty\md5 + diff --git a/util/installer/LICENSE.rtf b/util/installer/LICENSE.rtf index cfdf2c3b7..9f7f4c613 100644 --- a/util/installer/LICENSE.rtf +++ b/util/installer/LICENSE.rtf @@ -41,5 +41,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}http://half.sourceforge.net/"}}{\fldrslt{\ul\cf1 http://half.sourceforge.net/}}}\f0\fs22\line half.hpp distributed under the MIT License. Copyright (c) 2012-2019 Christian Rau.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://superluminal.eu/"}}{\fldrslt{\ul\cf1 https://superluminal.eu/}}}\f0\fs22\line Superluminal distributed under the BSD License. Copyright (c) 2019-2020 Superluminal.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/"}}{\fldrslt{\ul\cf1 https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/}}}\f0\fs22\line AGS distributed under the MIT License. Copyright (c) 2020 Advanced Micro Devices, Inc.\par +{\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5"}}{\fldrslt{\ul\cf1 http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5}}}\f0\fs22\line miniz released to the Public Domain by Alexander Peslyak.\par } diff --git a/util/test/demos/3rdparty/md5/README.md b/util/test/demos/3rdparty/md5/README.md new file mode 100644 index 000000000..e424b3ec1 --- /dev/null +++ b/util/test/demos/3rdparty/md5/README.md @@ -0,0 +1,38 @@ +Fetched from https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 on 2021-07-07 + +Public domain licensed: + +> This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. +> MD5 Message-Digest Algorithm (RFC 1321). +> +> Homepage: +> http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 +> +> Author: +> Alexander Peslyak, better known as Solar Designer +> +> This software was written by Alexander Peslyak in 2001. No copyright is +> claimed, and the software is hereby placed in the public domain. +> In case this attempt to disclaim copyright and place the software in the +> public domain is deemed null and void, then the software is +> Copyright (c) 2001 Alexander Peslyak and it is hereby released to the +> general public under the following terms: +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted. +> +> There's ABSOLUTELY NO WARRANTY, express or implied. +> +> (This is a heavily cut-down "BSD license".) +> +> This differs from Colin Plumb's older public domain implementation in that +> no exactly 32-bit integer data type is required (any 32-bit or wider +> unsigned integer data type will do), there's no compile-time endianness +> configuration, and the function prototypes match OpenSSL's. No code from +> Colin Plumb's implementation has been reused; this comment merely compares +> the properties of the two independent implementations. +> +> The primary goals of this implementation are portability and ease of use. +> It is meant to be fast, but not as fast as possible. Some known +> optimizations are not included to reduce source code size and avoid +> compile-time configuration. diff --git a/util/test/demos/3rdparty/md5/md5.c b/util/test/demos/3rdparty/md5/md5.c new file mode 100644 index 000000000..b235e17a5 --- /dev/null +++ b/util/test/demos/3rdparty/md5/md5.c @@ -0,0 +1,291 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/util/test/demos/3rdparty/md5/md5.h b/util/test/demos/3rdparty/md5/md5.h new file mode 100644 index 000000000..d9dac8f41 --- /dev/null +++ b/util/test/demos/3rdparty/md5/md5.h @@ -0,0 +1,54 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Added by baldurk, for C++ compatibility */ +#if defined(__cplusplus) +extern "C" { +#endif + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#if defined(__cplusplus) +}; // extern "C" +#endif + +#endif diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index e7ee200c6..8c9ff551c 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -27,13 +27,13 @@ #include "d3d12_test.h" #include #include "../3rdparty/lz4/lz4.h" +#include "../3rdparty/md5/md5.h" #include "../renderdoc_app.h" #include "../win32/win32_window.h" #include "dx/official/dxcapi.h" typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY1)(REFIID, void **); typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT, REFIID, void **); -using PFN_ENABLE_EXPERIMENTAL = decltype(&D3D12EnableExperimentalFeatures); typedef DXC_API_IMPORT HRESULT(__stdcall *pDxcCreateInstance)(REFCLSID rclsid, REFIID riid, LPVOID *ppv); @@ -95,13 +95,6 @@ void D3D12GraphicsTest::Prepare(int argc, char **argv) PFN_CREATE_DXGI_FACTORY2 createFactory2 = (PFN_CREATE_DXGI_FACTORY2)GetProcAddress(dxgi, "CreateDXGIFactory2"); - PFN_ENABLE_EXPERIMENTAL enableExperimental = - (PFN_ENABLE_EXPERIMENTAL)GetProcAddress(d3d12, "D3D12EnableExperimentalFeatures"); - - // try to enable unsigned shaders in case we don't get dxil.dll - if(enableExperimental) - enableExperimental(1, &D3D12ExperimentalShaderModels, NULL, NULL); - HRESULT hr = E_FAIL; if(createFactory2) @@ -659,6 +652,127 @@ void D3D12GraphicsTest::Present() m_Alloc->Reset(); } +void D3D12GraphicsTest::AddHashIfMissing(void *ByteCode, size_t BytecodeLength) +{ + struct FileHeader + { + uint32_t fourcc; + uint32_t hashValue[4]; + uint32_t containerVersion; + uint32_t fileLength; + }; + + if(BytecodeLength < sizeof(FileHeader)) + { + TEST_ERROR("Trying to hash corrupt DXBC container"); + return; + } + + FileHeader *header = (FileHeader *)ByteCode; + +#define MAKE_FOURCC(a, b, c, d) \ + (((uint32_t)(d) << 24) | ((uint32_t)(c) << 16) | ((uint32_t)(b) << 8) | (uint32_t)(a)) + + if(header->fourcc != MAKE_FOURCC('D', 'X', 'B', 'C')) + { + TEST_ERROR("Trying to hash corrupt DXBC container"); + return; + } + + if(header->fileLength != (uint32_t)BytecodeLength) + { + TEST_ERROR("Trying to hash corrupt DXBC container"); + return; + } + + if(header->hashValue[0] != 0 || header->hashValue[1] != 0 || header->hashValue[2] != 0 || + header->hashValue[3] != 0) + return; + + MD5_CTX md5ctx = {}; + MD5_Init(&md5ctx); + + // the hashable data starts immediately after the hash. + byte *data = (byte *)&header->containerVersion; + uint32_t length = uint32_t(BytecodeLength - offsetof(FileHeader, containerVersion)); + + // we need to know the number of bits for putting in the trailing padding. + uint32_t numBits = length * 8; + uint32_t numBitsPart2 = (numBits >> 2) | 1; + + // MD5 works on 64-byte chunks, process the first set of whole chunks, leaving 0-63 bytes left + // over + uint32_t leftoverLength = length % 64; + MD5_Update(&md5ctx, data, length - leftoverLength); + + data += length - leftoverLength; + + uint32_t block[16] = {}; + static_assert(sizeof(block) == 64, "Block is not properly sized for MD5 round"); + + // normally MD5 finishes by appending a 1 bit to the bitstring. Since we are only appending bytes + // this would be an 0x80 byte (the first bit is considered to be the MSB). Then it pads out with + // zeroes until it has 56 bytes in the last block and appends appends the message length as a + // 64-bit integer as the final part of that block. + // in other words, normally whatever is leftover from the actual message gets one byte appended, + // then if there's at least 8 bytes left we'll append the length. Otherwise we pad that block with + // 0s and create a new block with the length at the end. + // Or as the original RFC/spec says: padding is always performed regardless of whether the + // original buffer already ended in exactly a 56 byte block. + // + // The DXBC finalisation is slightly different (previous work suggests this is due to a bug in the + // original implementation and it was maybe intended to be exactly MD5?): + // + // The length provided in the padding block is not 64-bit properly: the second dword with the high + // bits is instead the number of nybbles(?) with 1 OR'd on. The length is also split, so if it's + // in + // a padding block the low bits are in the first dword and the upper bits in the last. If there's + // no padding block the low dword is passed in first before the leftovers of the message and then + // the upper bits at the end. + + // if the leftovers uses at least 56, we can't fit both the trailing 1 and the 64-bit length, so + // we need a padding block and then our own block for the length. + if(leftoverLength >= 56) + { + // pass in the leftover data padded out to 64 bytes with zeroes + MD5_Update(&md5ctx, data, leftoverLength); + + block[0] = 0x80; // first padding bit is 1 + MD5_Update(&md5ctx, block, 64 - leftoverLength); + + // the final block contains the number of bits in the first dword, and the weird upper bits + block[0] = numBits; + block[15] = numBitsPart2; + + // process this block directly, we're replacing the call to MD5_Final here manually + MD5_Update(&md5ctx, block, 64); + } + else + { + // the leftovers mean we can put the padding inside the final block. But first we pass the "low" + // number of bits: + MD5_Update(&md5ctx, &numBits, sizeof(numBits)); + + if(leftoverLength) + MD5_Update(&md5ctx, data, leftoverLength); + + uint32_t paddingBytes = 64 - leftoverLength - 4; + + // prepare the remainder of this block, starting with the 0x80 padding start right after the + // leftovers and the first part of the bit length above. + block[0] = 0x80; + // then add the remainder of the 'length' here in the final part of the block + memcpy(((byte *)block) + paddingBytes - 4, &numBitsPart2, 4); + + MD5_Update(&md5ctx, block, paddingBytes); + } + + header->hashValue[0] = md5ctx.a; + header->hashValue[1] = md5ctx.b; + header->hashValue[2] = md5ctx.c; + header->hashValue[3] = md5ctx.d; +} + std::vector D3D12GraphicsTest::GetBufferData(ID3D12ResourcePtr buffer, D3D12_RESOURCE_STATES state, uint32_t offset, uint64_t length) @@ -1110,6 +1224,9 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std:: dyn_CreateBlob((uint32_t)code->GetBufferSize(), &blob); memcpy(blob->GetBufferPointer(), code->GetBufferPointer(), code->GetBufferSize()); + + // if we didn't have dxil.dll around there won't be a hash, add it ourselves + AddHashIfMissing(blob->GetBufferPointer(), code->GetBufferSize()); } else { diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index 15a0fb3a8..9baf229bb 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -246,4 +246,7 @@ struct D3D12GraphicsTest : public GraphicsTest std::vector freeCommandBuffers; std::vector> pendingCommandBuffers; + +private: + void AddHashIfMissing(void *ByteCode, size_t BytecodeLength); }; diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index e44191fda..c7d9cb134 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -120,6 +120,7 @@ + @@ -340,6 +341,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index bfc393cb7..4c3735a75 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -601,6 +601,9 @@ D3D12\demos + + 3rdparty\md5 + @@ -669,6 +672,9 @@ {329344bd-312a-4cd6-b618-aadeb4eb13cb} + + {25327220-a428-4ea2-8894-300df4a61a33} + @@ -859,5 +865,8 @@ 3rdparty\ags + + 3rdparty\md5 + \ No newline at end of file