diff options
Diffstat (limited to 'source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp')
-rw-r--r-- | source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp | 180 |
1 files changed, 151 insertions, 29 deletions
diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp index 5d82ded..8e5d31b 100644 --- a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -235,16 +235,28 @@ struct RenderScriptRuntime::AllocationDetails } }; - // Header for reading and writing allocation contents - // to a binary file. + // The FileHeader struct specifies the header we use for writing allocations to a binary file. + // Our format begins with the ASCII characters "RSAD", identifying the file as an allocation dump. + // Member variables dims and hdr_size are then written consecutively, immediately followed by an instance of + // the ElementHeader struct. Because Elements can contain subelements, there may be more than one instance + // of the ElementHeader struct. With this first instance being the root element, and the other instances being + // the root's descendants. To identify which instances are an ElementHeader's children, each struct + // is immediately followed by a sequence of consecutive offsets to the start of its child structs. + // These offsets are 4 bytes in size, and the 0 offset signifies no more children. struct FileHeader { uint8_t ident[4]; // ASCII 'RSAD' identifying the file - uint16_t hdr_size; // Header size in bytes, for backwards compatability - uint16_t type; // DataType enum - uint32_t kind; // DataKind enum uint32_t dims[3]; // Dimensions - uint32_t element_size; // Size of a single element, including padding + uint16_t hdr_size; // Header size in bytes, including all element headers + }; + + struct ElementHeader + { + uint16_t type; // DataType enum + uint32_t kind; // DataKind enum + uint32_t element_size; // Size of a single element, including padding + uint16_t vector_size; // Vector width + uint32_t array_size; // Number of elements in array }; // Monotonically increasing from 1 @@ -286,7 +298,6 @@ struct RenderScriptRuntime::AllocationDetails } }; - const ConstString & RenderScriptRuntime::Element::GetFallbackStructName() { @@ -2084,37 +2095,62 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const // Cast start of buffer to FileHeader and use pointer to read metadata void* file_buffer = data_sp->GetBytes(); - const AllocationDetails::FileHeader* head = static_cast<AllocationDetails::FileHeader*>(file_buffer); + if (file_buffer == NULL || data_sp->GetByteSize() < + (sizeof(AllocationDetails::FileHeader) + sizeof(AllocationDetails::ElementHeader))) + { + strm.Printf("Error: File %s does not contain enough data for header", filename); + strm.EOL(); + return false; + } + const AllocationDetails::FileHeader* file_header = static_cast<AllocationDetails::FileHeader*>(file_buffer); - // Advance buffer past header - file_buffer = static_cast<uint8_t*>(file_buffer) + head->hdr_size; + // Check file starts with ascii characters "RSAD" + if (file_header->ident[0] != 'R' || file_header->ident[1] != 'S' || file_header->ident[2] != 'A' + || file_header->ident[3] != 'D') + { + strm.Printf("Error: File doesn't contain identifier for an RS allocation dump. Are you sure this is the correct file?"); + strm.EOL(); + return false; + } + + // Look at the type of the root element in the header + AllocationDetails::ElementHeader root_element_header; + memcpy(&root_element_header, static_cast<uint8_t*>(file_buffer) + sizeof(AllocationDetails::FileHeader), + sizeof(AllocationDetails::ElementHeader)); if (log) log->Printf("RenderScriptRuntime::LoadAllocation - header type %u, element size %u", - head->type, head->element_size); + root_element_header.type, root_element_header.element_size); // Check if the target allocation and file both have the same number of bytes for an Element - if (*alloc->element.datum_size.get() != head->element_size) + if (*alloc->element.datum_size.get() != root_element_header.element_size) { strm.Printf("Warning: Mismatched Element sizes - file %u bytes, allocation %u bytes", - head->element_size, *alloc->element.datum_size.get()); + root_element_header.element_size, *alloc->element.datum_size.get()); strm.EOL(); } - // Check if the target allocation and file both have the same integral type - const unsigned int type = static_cast<unsigned int>(*alloc->element.type.get()); - if (type != head->type) + // Check if the target allocation and file both have the same type + const unsigned int alloc_type = static_cast<unsigned int>(*alloc->element.type.get()); + const unsigned int file_type = root_element_header.type; + + if (file_type > Element::RS_TYPE_FONT) + { + strm.Printf("Warning: File has unknown allocation type"); + strm.EOL(); + } + else if (alloc_type != file_type) { // Enum value isn't monotonous, so doesn't always index RsDataTypeToString array - unsigned int printable_target_type_index = type; - unsigned int printable_head_type_index = head->type; - if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT) + unsigned int printable_target_type_index = alloc_type; + unsigned int printable_head_type_index = file_type; + if (alloc_type >= Element::RS_TYPE_ELEMENT && alloc_type <= Element::RS_TYPE_FONT) printable_target_type_index = static_cast<Element::DataType>( - (type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); + (alloc_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); - if (head->type >= Element::RS_TYPE_ELEMENT && head->type <= Element::RS_TYPE_FONT) + if (file_type >= Element::RS_TYPE_ELEMENT && file_type <= Element::RS_TYPE_FONT) printable_head_type_index = static_cast<Element::DataType>( - (head->type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); + (file_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); const char* file_type_cstr = AllocationDetails::RsDataTypeToString[printable_head_type_index][0]; const char* target_type_cstr = AllocationDetails::RsDataTypeToString[printable_target_type_index][0]; @@ -2124,8 +2160,11 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const strm.EOL(); } + // Advance buffer past header + file_buffer = static_cast<uint8_t*>(file_buffer) + file_header->hdr_size; + // Calculate size of allocation data in file - size_t length = data_sp->GetByteSize() - head->hdr_size; + size_t length = data_sp->GetByteSize() - file_header->hdr_size; // Check if the target allocation and file both have the same total data size. const unsigned int alloc_size = *alloc->size.get(); @@ -2154,6 +2193,62 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const return true; } +// Function takes as parameters a byte buffer, which will eventually be written to file as the element header, +// an offset into that buffer, and an Element that will be saved into the buffer at the parametrised offset. +// Return value is the new offset after writing the element into the buffer. +// Elements are saved to the file as the ElementHeader struct followed by offsets to the structs of all the element's children. +size_t +RenderScriptRuntime::PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer, size_t offset, const Element& elem) +{ + // File struct for an element header with all the relevant details copied from elem. + // We assume members are valid already. + AllocationDetails::ElementHeader elem_header; + elem_header.type = *elem.type.get(); + elem_header.kind = *elem.type_kind.get(); + elem_header.element_size = *elem.datum_size.get(); + elem_header.vector_size = *elem.type_vec_size.get(); + elem_header.array_size = elem.array_size.isValid() ? *elem.array_size.get() : 0; + const size_t elem_header_size = sizeof(AllocationDetails::ElementHeader); + + // Copy struct into buffer and advance offset + // We assume that header_buffer has been checked for NULL before this method is called + memcpy(header_buffer.get() + offset, &elem_header, elem_header_size); + offset += elem_header_size; + + // Starting offset of child ElementHeader struct + size_t child_offset = offset + ((elem.children.size() + 1) * sizeof(uint32_t)); + for (const RenderScriptRuntime::Element& child : elem.children) + { + // Recursively populate the buffer with the element header structs of children. + // Then save the offsets where they were set after the parent element header. + memcpy(header_buffer.get() + offset, &child_offset, sizeof(uint32_t)); + offset += sizeof(uint32_t); + + child_offset = PopulateElementHeaders(header_buffer, child_offset, child); + } + + // Zero indicates no more children + memset(header_buffer.get() + offset, 0, sizeof(uint32_t)); + + return child_offset; +} + +// Given an Element object this function returns the total size needed in the file header to store the element's details. +// Taking into account the size of the element header struct, plus the offsets to all the element's children. +// Function is recursive so that the size of all ancestors is taken into account. +size_t +RenderScriptRuntime::CalculateElementHeaderSize(const Element& elem) +{ + size_t size = (elem.children.size() + 1) * sizeof(uint32_t); // Offsets to children plus zero terminator + size += sizeof(AllocationDetails::ElementHeader); // Size of header struct with type details + + // Calculate recursively for all descendants + for (const Element& child : elem.children) + size += CalculateElementHeaderSize(child); + + return size; +} + // Function copies allocation contents into a binary file. // This file can then be loaded later into a different allocation. // There is a header, FileHeader, before the allocation data containing meta-data. @@ -2209,17 +2304,44 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const // Create the file header AllocationDetails::FileHeader head; head.ident[0] = 'R'; head.ident[1] = 'S'; head.ident[2] = 'A'; head.ident[3] = 'D'; - head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader)); - head.type = static_cast<uint16_t>(*alloc->element.type.get()); - head.kind = static_cast<uint32_t>(*alloc->element.type_kind.get()); head.dims[0] = static_cast<uint32_t>(alloc->dimension.get()->dim_1); head.dims[1] = static_cast<uint32_t>(alloc->dimension.get()->dim_2); head.dims[2] = static_cast<uint32_t>(alloc->dimension.get()->dim_3); - head.element_size = static_cast<uint32_t>(*alloc->element.datum_size.get()); + + const size_t element_header_size = CalculateElementHeaderSize(alloc->element); + assert((sizeof(AllocationDetails::FileHeader) + element_header_size) < UINT16_MAX && "Element header too large"); + head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader) + element_header_size); // Write the file header size_t num_bytes = sizeof(AllocationDetails::FileHeader); - Error err = file.Write(static_cast<const void*>(&head), num_bytes); + if (log) + log->Printf("RenderScriptRuntime::SaveAllocation - Writing File Header, 0x%zX bytes", num_bytes); + + Error err = file.Write(&head, num_bytes); + if (!err.Success()) + { + strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); + strm.EOL(); + return false; + } + + // Create the headers describing the element type of the allocation. + std::shared_ptr<uint8_t> element_header_buffer(new uint8_t[element_header_size]); + if (element_header_buffer == nullptr) + { + strm.Printf("Internal Error: Couldn't allocate %zu bytes on the heap", element_header_size); + strm.EOL(); + return false; + } + + PopulateElementHeaders(element_header_buffer, 0, alloc->element); + + // Write headers for allocation element type to file + num_bytes = element_header_size; + if (log) + log->Printf("RenderScriptRuntime::SaveAllocation - Writing Element Headers, 0x%zX bytes", num_bytes); + + err = file.Write(element_header_buffer.get(), num_bytes); if (!err.Success()) { strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); @@ -2230,7 +2352,7 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const // Write allocation data to file num_bytes = static_cast<size_t>(*alloc->size.get()); if (log) - log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, (void*) buffer.get()); + log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%zX bytes", num_bytes); err = file.Write(buffer.get(), num_bytes); if (!err.Success()) |