From 04ca50ba63904edd280e7bdc9595edd8bf0bd3bd Mon Sep 17 00:00:00 2001 From: akharlamov Date: Tue, 11 Dec 2018 17:04:58 -0800 Subject: [PATCH] Adding utility code for VK code generator. ExtObject extends the SDObject structure with helper methods. Intervals are used for memory region tracking. Change-Id: I38198c0a096ed838d527b6526216fb28326ebc89 --- renderdoc/api/replay/stringise.h | 4 +- renderdoc/api/replay/structured_data.h | 86 ++++++++- renderdoc/core/intervals.h | 171 ++++++++++++++++++ renderdoc/driver/d3d11/d3d11_stringise.cpp | 2 +- renderdoc/renderdoc.vcxproj | 2 + renderdoc/renderdoc.vcxproj.filters | 12 ++ .../serialise/codecs/vk_cpp_codec_common.h | 169 +++++++++++++++++ 7 files changed, 436 insertions(+), 10 deletions(-) create mode 100644 renderdoc/core/intervals.h create mode 100644 renderdoc/serialise/codecs/vk_cpp_codec_common.h diff --git a/renderdoc/api/replay/stringise.h b/renderdoc/api/replay/stringise.h index 2af99a1f3..ad4d27f31 100644 --- a/renderdoc/api/replay/stringise.h +++ b/renderdoc/api/replay/stringise.h @@ -51,7 +51,7 @@ std::string ToStr(const T &el) // helper macros for common enum switch #define BEGIN_ENUM_STRINGISE(type) \ using enumType = type; \ - static const char unknown_prefix[] = #type "<"; \ + static const char unknown_prefix[] = #type "("; \ static_assert(std::is_same::value, \ "Type in macro doesn't match el"); \ (void)(enumType) el; \ @@ -78,7 +78,7 @@ std::string ToStr(const T &el) // end enum switches #define END_ENUM_STRINGISE() \ } \ - return unknown_prefix + ToStr((uint32_t)el) + ">"; + return unknown_prefix + ToStr((uint32_t)el) + ")"; // helper macros for common bitfield check-and-append #define BEGIN_BITFIELD_STRINGISE(type) \ diff --git a/renderdoc/api/replay/structured_data.h b/renderdoc/api/replay/structured_data.h index 96dd9fee5..7947ac85f 100644 --- a/renderdoc/api/replay/structured_data.h +++ b/renderdoc/api/replay/structured_data.h @@ -380,7 +380,6 @@ struct SDObject for(size_t i = 0; i < data.children.size(); i++) if(data.children[i]->name == childName) return data.children[i]; - return NULL; } @@ -389,10 +388,11 @@ struct SDObject { if(index < data.children.size()) return data.children[index]; - return NULL; } + DOCUMENT("Get the number of child objects."); + inline size_t NumChildren() const { return data.children.size(); } DOCUMENT("Get a ``list`` of :class:`SDObject` children."); inline StructuredObjectList &GetChildren() { return data.children; } #if !defined(SWIG) @@ -413,7 +413,7 @@ struct SDObject } inline double AsDouble() const { return data.basic.d; } inline float AsFloat() const { return (float)data.basic.d; } - inline float AsChar() const { return (float)data.basic.c; } + inline char AsChar() const { return data.basic.c; } inline std::string AsString() const { return data.str; } inline uint64_t AsUInt64() const { return (uint64_t)data.basic.u; } inline int64_t AsInt64() const { return (int64_t)data.basic.i; } @@ -423,6 +423,72 @@ struct SDObject inline int16_t AsInt16() const { return (int16_t)data.basic.i; } inline uint8_t AsUInt8() const { return (uint8_t)data.basic.u; } inline int8_t AsInt8() const { return (int8_t)data.basic.i; } + inline double &Double() { return data.basic.d; } + inline uint64_t &UInt64() { return data.basic.u; } + inline int64_t &Int64() { return data.basic.i; } + inline bool IsStruct() const { return type.basetype == SDBasic::Struct; } + inline bool IsNULL() const + { + return type.basetype == SDBasic::Null || (IsArray() && NumChildren() == 0) || + (IsString() && (type.flags & SDTypeFlags::NullString)); + } + inline bool IsUInt() const { return type.basetype == SDBasic::UnsignedInteger; } + inline bool IsInt() const { return type.basetype == SDBasic::SignedInteger; } + inline bool IsFloat() const { return type.basetype == SDBasic::Float; } + inline bool IsString() const { return type.basetype == SDBasic::String; } + inline bool IsArray() const { return type.basetype == SDBasic::Array; } + inline bool IsFixedArray(uint64_t size = 0) const + { + return IsArray() && (type.flags & SDTypeFlags::FixedArray) && + (size > 0 ? NumChildren() <= size : true); + } + inline bool IsVariableArray() const + { + return IsArray() && ((type.flags & SDTypeFlags::FixedArray) == SDTypeFlags::NoFlags); + } + inline bool IsEnum() const { return type.basetype == SDBasic::Enum; } + inline bool IsBuffer() const { return type.basetype == SDBasic::Buffer; } + inline bool IsPointer() const + { + return (type.flags & SDTypeFlags::Nullable) && (NumChildren() != 0); + } + inline bool IsResource() const { return type.basetype == SDBasic::Resource; } + inline bool IsUnion() const + { + return (type.basetype == SDBasic::Struct) && (type.flags & SDTypeFlags::Union); + } + inline bool IsSimpleType() const + { + return IsNULL() || (!IsStruct() && !IsArray() && !IsPointer() && !IsUnion()); + } + + // Is it possible to fully inline the data structure declaration? + inline bool IsInlineable() const + { + // if it has elements that are not inlineable, return false. + for(size_t i = 0; i < NumChildren(); i++) + if(!GetChild(i)->IsInlineable()) + return false; + if((IsPointer() || IsVariableArray()) && !IsNULL()) + return false; + if(IsUnion()) + return false; + return true; + } + const char *Type() const { return type.name.c_str(); } + const char *Name() const { return name.c_str(); } + SDObject *SetTypeName(const char *customTypeName) + { + type.name = customTypeName; + return this; + } + SDObject *SetCustomString(const char *customString) + { + data.str = customString; + type.flags = SDTypeFlags::HasCustomString; + return this; + } + #endif // these are common to both python and C++ @@ -676,10 +742,16 @@ inline SDObject *makeSDStruct(const char *name, const char *structtype) // concept of different width types like 32-bit vs 64-bit ints #if !defined(SWIG) -#define SDOBJECT_MAKER(basetype, makeSDFunc) \ - inline SDObject *makeSDObject(const char *name, basetype value) \ - { \ - return makeSDFunc(name, value); \ +#define SDOBJECT_MAKER(basetype, makeSDFunc) \ + inline SDObject *makeSDObject(const char *name, basetype value, const char *customString = NULL, \ + const char *customTypeName = NULL) \ + { \ + SDObject *ptr = makeSDFunc(name, value); \ + if(customString) \ + ptr->SetCustomString(customString); \ + if(customTypeName) \ + ptr->SetTypeName(customTypeName); \ + return ptr; \ } SDOBJECT_MAKER(int64_t, makeSDInt64); diff --git a/renderdoc/core/intervals.h b/renderdoc/core/intervals.h new file mode 100644 index 000000000..9ac1f8cee --- /dev/null +++ b/renderdoc/core/intervals.h @@ -0,0 +1,171 @@ +/****************************************************************************** +* The MIT License (MIT) +* +* Copyright (c) 2018 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 +#include + +template +struct Intervals; + +template +struct IntervalsIter +{ + friend struct Intervals; + +private: + typename std::map::iterator iter; + std::map *owner; + IntervalsIter(std::map *owner, typename std::map::iterator iter) + : iter(iter), owner(owner) + { + } + typename std::map::iterator unwrap() { return iter; } +public: + IntervalsIter(const IntervalsIter &src) : owner(src.owner), iter(src.iter) {} + IntervalsIter &operator++() + { + ++iter; + return *this; + } + IntervalsIter operator++(int) + { + IntervalsIter tmp(*this); + operator++(); + return tmp; + } + IntervalsIter &operator--() + { + --iter; + return *this; + } + IntervalsIter operator--(int) + { + IntervalsIter tmp(*this); + operator--(); + return tmp; + } + bool operator==(const IntervalsIter &rhs) const { return iter == rhs.iter && owner == rhs.owner; } + bool operator!=(const IntervalsIter &rhs) const { return iter != rhs.iter && owner == rhs.owner; } + IntervalsIter &operator=(const IntervalsIter &rhs) + { + iter = rhs.iter; + owner = rhs.owner; + return *this; + } + inline uint64_t start() const { return iter->first; } + inline uint64_t end() const + { + typename std::map::iterator next = iter; + next++; + if(next == owner->end()) + { + return UINT64_MAX; + } + return next->first; + } + inline const T &value() const { return iter->second; } + inline void setValue(uint64_t aStart, uint64_t aEnd, const T &aValue) + { + T old_value = this->value(); + if((aValue == old_value || aEnd <= start()) && end() <= aStart) + { + // The value is unchanged, or the specified interval is disjoint from this interval. + return; + } + + // Add a new endpoint for aStart, if necessary, and update iter's value to aValue. + if(start() < aStart) + { + // The updated portion of this interval begins at aStart. + iter = owner->insert(std::pair(aStart, aValue)).first; + } + else + { + // The updated portion of this interval begins at start() + iter->second = aValue; + } + + // Add a new endpoint for aEnd, if necessary. + if(aEnd < end()) + { + owner->insert(std::pair(aEnd, old_value)); + } + + // Merge with preceding interval, if necessary + if(iter != owner->begin()) + { + typename std::map::iterator prev_it = iter; + prev_it--; + if(prev_it->second == iter->second) + { + owner->erase(iter); + iter = prev_it; + } + } + + // Merge with succeding interval, if necessary + typename std::map::iterator next_it = iter; + next_it++; + if(next_it != owner->end()) + { + if(next_it->second == iter->second) + { + owner->erase(next_it); + } + } + } +}; + +template +struct Intervals +{ +private: + std::map StartPoints; + +public: +private: + IntervalsIter Wrap(typename std::map::iterator iter) + { + return IntervalsIter(&StartPoints, iter); + } + +public: + Intervals() : StartPoints{{0, T()}} {} + IntervalsIter begin() { return Wrap(StartPoints.begin()); } + IntervalsIter end() { return Wrap(StartPoints.end()); } + // finds the interval containing `x`. + IntervalsIter find(uint64_t x) + { + // Find the first interval which starts AFTER `x` + typename std::map::iterator it = StartPoints.upper_bound(x); + // Because the first interval always starts at 0, the found interval cannot be the first + // interval + RDCASSERT(it != StartPoints.begin()); + // Move back 1 interval, to find the last interval that starts no later than `range.start`. + // This is the interval that contains the point + it--; + return Wrap(it); + } +}; \ No newline at end of file diff --git a/renderdoc/driver/d3d11/d3d11_stringise.cpp b/renderdoc/driver/d3d11/d3d11_stringise.cpp index cf714a1ee..39d4af9a2 100644 --- a/renderdoc/driver/d3d11/d3d11_stringise.cpp +++ b/renderdoc/driver/d3d11/d3d11_stringise.cpp @@ -777,7 +777,7 @@ std::string DoStringise(const D3D11_LOGIC_OP &el) TEST_CASE("D3D11 ToStr", "[tostr][d3d]") { CHECK(ToStr(D3D11_LOGIC_OP_SET) == "D3D11_LOGIC_OP_SET"); - CHECK(ToStr(D3D11_LOGIC_OP(9999)) == "D3D11_LOGIC_OP<9999>"); + CHECK(ToStr(D3D11_LOGIC_OP(9999)) == "D3D11_LOGIC_OP(9999)"); CHECK(ToStr(D3D11_BIND_FLAG(D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER)) == "D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER"); diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 863aefaf3..b0aa228b4 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -183,6 +183,7 @@ + @@ -213,6 +214,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 72c7357c4..364cfce9d 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -130,6 +130,12 @@ {5994c355-880b-45b9-b17a-2e9e1fc2044a} + + {27f41754-8fa8-4a78-afb8-f63a32473540} + + + {ee6a7f7e-becf-43f0-8c7f-e0109a364ebb} + @@ -459,6 +465,12 @@ API\Replay + + Core + + + Common\Serialise\Codecs\cpp_codec\vulkan + diff --git a/renderdoc/serialise/codecs/vk_cpp_codec_common.h b/renderdoc/serialise/codecs/vk_cpp_codec_common.h new file mode 100644 index 000000000..58c51385a --- /dev/null +++ b/renderdoc/serialise/codecs/vk_cpp_codec_common.h @@ -0,0 +1,169 @@ +/****************************************************************************** +* The MIT License (MIT) +* +* Copyright (c) 2018 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 + +#include +#include +#include + +#include "serialise/rdcfile.h" + +namespace vk_cpp_codec +{ +inline const char *Type(SDObject *ptr) +{ + // (akharlamov) Moving this to filtering stage in TraceTracker class + // isn't trivial. Patching type.name, when done in Type() is only applied + // for SDObjects that store data structures, and patching them during filtering + // stage means the filter stage would need to touch on most of Vulkan API OR + // crawl through the entire SDObject list and patch every object ignoring Vulkan + // specifics. + + // Vulkan doesn't use std::string, so need to cast it to const char * + if(ptr->IsString() || ptr->type.name == "string") + return "const char* "; + + return ptr->type.name.c_str(); +} + +inline std::string ValueStr(SDObject *ptr) +{ + RDCASSERT(ptr->IsSimpleType()); + std::string result; + + if(ptr->IsBuffer()) + { + std::string buf_name = ptr->AsString(); + RDCASSERT(!buf_name.empty()); + // A value for for a Buffer is it's $name.data(). + result = buf_name + ".data()"; + // just-in-time fix for vkCreatShaderModule pCode variable + if(ptr->name == "pCode") + result = "(const uint32_t*) " + result; + } + else if(ptr->IsNULL()) + { + result = "NULL"; + } + else if(ptr->IsUInt()) + { + result = std::to_string(ptr->AsUInt64()) + "u"; + } + else if(ptr->IsInt()) + { + result = std::to_string(ptr->AsInt64()); + } + else if(ptr->IsFloat()) + { + if(isnan(ptr->data.basic.d)) + ptr->data.basic.d = 1.0f; + result = std::to_string(ptr->AsDouble()) + "f"; + } + else if(ptr->IsEnum()) + { + result = ptr->data.str; + } + else if(ptr->IsString()) + { + std::string escaped; + escaped.reserve(ptr->data.str.size()); + for(char c : ptr->data.str) + { + switch(c) + { + case '\a': escaped += "\\a"; break; + case '\b': escaped += "\\b"; break; + case '\f': escaped += "\\f"; break; + case '\n': escaped += "\\n"; break; + case '\r': escaped += "\\r"; break; + case '\t': escaped += "\\t"; break; + case '\v': escaped += "\\v"; break; + + case '"': + case '\\': + escaped.push_back('\\'); + escaped.push_back(c); + break; + + default: + if(c < 32 || c > 127) + { + char buf[8] = {}; + snprintf(buf, sizeof(buf), "\\x%02X", c); + escaped += buf; + } + else + { + escaped.push_back(c); + } + break; + } + } + result = std::string("\"") + escaped + std::string("\""); + } + return result; +} + +inline uint64_t CanonicalUnionBranch(SDObject *ptr) +{ + if(ptr->type.name == "VkClearValue") + { + return 0; // Use `color` + } + else if(ptr->type.name == "VkClearColorValue") + { + return 2; // Use `uint32` + } + // This function must be modified further to return the index of the canonical branch, which + // should be chosen so that it's size is equal to the size of the entire union, and so that the + // values can be represented exactly (e.g., not floating point). + RDCERR("Attempting to output an unknown union type %s", ptr->type.name.c_str()); + return 0; +} + +typedef std::vector SDObjectVec; +typedef SDObjectVec::iterator SDObjectVecIter; + +typedef std::map SDObjectIDMap; +typedef SDObjectIDMap::iterator SDObjectIDMapIter; +typedef std::pair SDObjectIDMapPair; + +typedef std::map SDObjectVecIDMap; +typedef SDObjectVecIDMap::iterator SDObjectVecIDMapIter; +typedef std::pair SDObjectVecIDMapPair; + +typedef std::vector SDChunkVec; +typedef SDChunkVec::iterator SDChunkVecIter; + +typedef std::map SDChunkIDMap; +typedef SDChunkIDMap::iterator SDChunkIDMapIter; +typedef std::pair SDChunkIDMapPair; + +typedef std::map SDChunkVecIDMap; +typedef SDChunkVecIDMap::iterator SDChunkVecIDMapIter; +typedef std::pair SDChunkVecIDMapPair; + +} // namespace vk_cpp_codec \ No newline at end of file