Add option --out_dir to specify output dir
Also copies the filepath.* libraries from glbench for better
file path management.
BUG=chromium:936705
TEST=Compile and run
Change-Id: I3ef5a16964637501d7007076a73ce6be7966ddc8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vkbench/+/2533451
Auto-Submit: Po-Hsien Wang <pwang@chromium.org>
Reviewed-by: Ilja H. Friedel <ihf@chromium.org>
Commit-Queue: Ilja H. Friedel <ihf@chromium.org>
Tested-by: Ilja H. Friedel <ihf@chromium.org>
diff --git a/src/clearTest.cc b/src/clearTest.cc
index 67aec4e..f8b2488 100644
--- a/src/clearTest.cc
+++ b/src/clearTest.cc
@@ -101,7 +101,7 @@
frame_buffer_ = device.createFramebuffer(frame_buffer_info);
}
-void ClearTest::SaveImage(std::string filename) const {
+void ClearTest::SaveImage(FilePath file_path) const {
const vk::Device device = vk->GetDevice();
vkImage* dest = vk->GetReadableImage(img_, img_size_, img_format_);
DEFER(delete dest);
@@ -110,10 +110,9 @@
const char* data =
(const char*)device.mapMemory(dest->GetMemory(), 0, VK_WHOLE_SIZE);
data += sub_resource_layout.offset;
- SavePPM(filename, data, img_size_.width, img_size_.height,
+ SavePPM(file_path, data, img_size_.width, img_size_.height,
sub_resource_layout.rowPitch);
-
- DEBUG("Saved image to %s", filename.c_str());
+ DEBUG("Saved image to %s", file_path.value().c_str());
}
} // namespace vkbench
diff --git a/src/clearTest.h b/src/clearTest.h
index 124c69e..3650cec 100644
--- a/src/clearTest.h
+++ b/src/clearTest.h
@@ -24,7 +24,7 @@
virtual double FormatMeasurement(double time) override {
return img_size_.width * img_size_.height / time;
}
- void SaveImage(std::string filename) const override;
+ void SaveImage(FilePath file_path) const override;
protected:
void Initialize() override;
diff --git a/src/drawSizeTest.cc b/src/drawSizeTest.cc
index 26315a0..a376abb 100644
--- a/src/drawSizeTest.cc
+++ b/src/drawSizeTest.cc
@@ -4,7 +4,7 @@
#include "drawSizeTest.h"
-extern std::string g_spirv_dir;
+extern FilePath g_spirv_dir;
namespace vkbench {
void DrawSizeTest::Initialize() {
@@ -79,9 +79,9 @@
void DrawSizeTest::CreateGraphicsPipeline() {
std::string vert_shader_code =
- readFile(g_spirv_dir + "drawSizeTest.vert.spv");
+ readShaderFile("drawSizeTest.vert.spv");
std::string frag_shader_code =
- readFile(g_spirv_dir + "drawSizeTest.frag.spv");
+ readShaderFile("drawSizeTest.frag.spv");
vk::Device device = vk->GetDevice();
vk::ShaderModule vert_shader_module =
@@ -168,7 +168,7 @@
frame_buffer_ = device.createFramebuffer(frame_buffer_info);
}
-void DrawSizeTest::SaveImage(std::string filename) const {
+void DrawSizeTest::SaveImage(FilePath file_path) const {
const vk::Device device = vk->GetDevice();
vkImage* dest = vk->GetReadableImage(img_, img_size_, img_format_);
DEFER(delete dest);
@@ -177,10 +177,9 @@
const char* data =
(const char*)device.mapMemory(dest->GetMemory(), 0, VK_WHOLE_SIZE);
data += sub_resource_layout.offset;
- SavePPM(filename, data, img_size_.width, img_size_.height,
+ SavePPM(file_path, data, img_size_.width, img_size_.height,
sub_resource_layout.rowPitch);
-
- DEBUG("Saved image to %s", filename.c_str());
+ DEBUG("Saved image to %s", file_path.value().c_str());
}
} // namespace vkbench
diff --git a/src/drawSizeTest.h b/src/drawSizeTest.h
index 214a679..743fc3c 100644
--- a/src/drawSizeTest.h
+++ b/src/drawSizeTest.h
@@ -22,7 +22,7 @@
const char* Name() const override { return name_; }
const char* Desp() const override { return desp_; }
const char* Unit() const override { return "us"; }
- void SaveImage(std::string filename) const override;
+ void SaveImage(FilePath file_path) const override;
protected:
void Initialize() override;
diff --git a/src/filepath.cc b/src/filepath.cc
new file mode 100644
index 0000000..cd51c58
--- /dev/null
+++ b/src/filepath.cc
@@ -0,0 +1,166 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "filepath.h"
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+#include "utils.h"
+
+inline void AppendToString(std::string* target, std::string& source) {
+ target->append(source);
+}
+
+FilePath FilePath::DirName() {
+ FilePath new_path(path_);
+
+ std::string::size_type last_separator =
+ new_path.path_.find_last_of(kSeparators, std::string::npos);
+
+ unsigned int letter = 0;
+ if (last_separator == std::string::npos) {
+ // path_ is in the current directory.
+ new_path.path_.resize(letter + 1);
+ } else if (last_separator == letter + 1) {
+ // path_ is in the root directory.
+ new_path.path_.resize(letter + 2);
+ } else if (last_separator == letter + 2 &&
+ IsSeparator(new_path.path_[letter + 1])) {
+ // path_ is in "//" (possibly with a drive letter); leave the double
+ // separator intact indicating alternate root.
+ new_path.path_.resize(letter + 3);
+ } else if (last_separator != 0) {
+ // path_ is somewhere else, trim the basename.
+ new_path.path_.resize(last_separator);
+ }
+ new_path.StripTrailingSeparatorsInternal();
+ if (!new_path.path_.length())
+ new_path.path_ = kCurrentDirectory;
+
+ return new_path;
+}
+
+const std::string& FilePath::value() const {
+ return this->path_;
+}
+
+bool FilePath::IsSeparator(char character) {
+ for (size_t i = 0; i < strlen(kSeparators) - 1; ++i) {
+ if (character == kSeparators[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+FilePath FilePath::Append(const FilePath& path) {
+ // return FilePath(this->path_ + path.path_);
+ std::string component = path.value();
+ std::string appended = component;
+ std::string without_nuls;
+
+ std::string::size_type nul_pos = component.find(kStringTerminator);
+ if (nul_pos != std::string::npos) {
+ // without_nuls = component.substr(0, nul_pos);
+ appended = std::string(without_nuls);
+ }
+
+ // DCHECK(appended.length() <= 0 || appended[0] !=
+ // this->separator.c_str()[0]);
+ if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
+ // Append normally doesn't do any normalization, but as a special case,
+ // when appending to kCurrentDirectory, just return a new path for the
+ // component argument. Appending component to kCurrentDirectory would
+ // serve no purpose other than needlessly lengthening the path, and
+ // it's likely in practice to wind up with FilePath objects containing
+ // only kCurrentDirectory when calling DirName on a single relative path
+ // component.
+ return FilePath(appended);
+ }
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // Don't append a separator if the path is empty (indicating the current
+ // directory) or if the path component is empty (indicating nothing to
+ // append).
+ if (!appended.empty() && !new_path.path_.empty()) {
+ // Don't append a separator if the path still ends with a trailing
+ // separator after stripping (indicating the root directory).
+ char tmp = new_path.path_.back();
+ if (!IsSeparator(tmp)) {
+ // Don't append a separator if the path is just a drive letter.
+ if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
+ new_path.path_.append(1, kSeparators[0]);
+ }
+ }
+ }
+ AppendToString(&new_path.path_, appended);
+ return new_path;
+}
+
+void FilePath::StripTrailingSeparatorsInternal() {
+ // If there is no drive letter, start will be 1, which will prevent
+ // stripping the leading separator if there is only one separator. If there
+ // is a drive letter, start will be set appropriately to prevent stripping
+ // the first separator following the drive letter, if a separator
+ // immediately follows the drive letter.
+ std::string::size_type start = FindDriveLetter(path_) + 2;
+
+ std::string::size_type last_stripped = std::string::npos;
+ for (std::string::size_type pos = path_.length();
+ pos > start && IsSeparator(path_[pos - 1]); --pos) {
+ // If the string only has two separators and they're at the beginning,
+ // don't strip them, unless the string began with more than two
+ // separators.
+ if (pos != start + 1 || last_stripped == start + 2 ||
+ !IsSeparator(path_[start - 1])) {
+ path_.resize(pos - 1);
+ last_stripped = pos;
+ }
+ }
+}
+
+std::string::size_type FindDriveLetter(std::string path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // This is dependent on an ASCII-based character set, but that's a
+ // reasonable assumption. iswalpha can be too inclusive here.
+ if (path.length() >= 2 && path[1] == L':' &&
+ ((path[0] >= L'A' && path[0] <= L'Z') ||
+ (path[0] >= L'a' && path[0] <= L'z'))) {
+ return 1;
+ }
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+ return std::string::npos;
+}
+
+bool CreateDirectory(FilePath& full_path) {
+ std::vector<FilePath> subpaths;
+
+ // Collect a list of all parent directories.
+ FilePath last_path = full_path;
+ subpaths.push_back(full_path);
+ for (FilePath path = full_path.DirName(); path.value() != last_path.value();
+ path = path.DirName()) {
+ subpaths.push_back(path);
+ last_path = path;
+ }
+
+ // Iterate through the parents and create the missing ones.
+ for (auto i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
+ if (check_dir_existence(i->value().c_str()))
+ continue;
+ if (mkdir(i->value().c_str(), 0700) == 0)
+ continue;
+ // Mkdir failed, but it might have failed with EEXIST, or some other error
+ // due to the the directory appearing out of thin air. This can occur if
+ // two processes are trying to create the same file system tree at the same
+ // time. Check to see if it exists and make sure it is a directory.
+ if (!check_dir_existence(i->value().c_str())) {
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/src/filepath.h b/src/filepath.h
new file mode 100644
index 0000000..00e6144
--- /dev/null
+++ b/src/filepath.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BENCH_GL_FILES_PATH_H_
+#define BENCH_GL_FILES_PATH_H_
+
+#include <iostream>
+
+class FilePath {
+ public:
+ FilePath() { this->path_ = std::string(""); }
+ FilePath(const FilePath& that) { this->path_ = that.path_; }
+ FilePath(std::string path) { this->path_ = path; }
+ FilePath(const char* path) { this->path_ = path; }
+ ~FilePath() = default;
+
+ FilePath DirName();
+ const std::string& value() const;
+ bool IsSeparator(char character);
+ FilePath Append(const FilePath& path);
+ void StripTrailingSeparatorsInternal();
+
+ private:
+ std::string path_;
+ char kStringTerminator = '\0';
+ char kSeparators[2] = "/";
+ char kCurrentDirectory[2] = ".";
+};
+bool CreateDirectory(FilePath&);
+std::string::size_type FindDriveLetter(std::string path);
+
+#endif // BENCH_GL_FILES_PATH_H_
diff --git a/src/main.cc b/src/main.cc
index b92b9e0..fcdce34 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -12,6 +12,7 @@
#include "clearTest.h"
#include "constant.h"
#include "drawSizeTest.h"
+#include "filepath.h"
#include "submitTest.h"
#include "utils.h"
@@ -27,7 +28,9 @@
// possible.
int g_hasty = false;
// g_spirv_dir is the path to the folder contains spirv code for test.
-std::string g_spirv_dir = "shaders/";
+FilePath g_spirv_dir = FilePath("shaders");
+// g_out_dir is the path to the folder to store the output image.
+FilePath g_out_dir = FilePath("");
// kLongOptions defines the options for argument options.
const static struct option kLongOptions[] = {
@@ -35,6 +38,7 @@
{"tests", required_argument, nullptr, 't'},
{"blacklist", required_argument, nullptr, 'b'},
{"spirv_dir", required_argument, nullptr, 's'},
+ {"out_dir", required_argument, nullptr, 'o'},
{"help", no_argument, nullptr, 'h'},
{"list", no_argument, &g_list, 1},
{"vlayer", no_argument, &g_vlayer, 1},
@@ -101,7 +105,7 @@
score = test->FormatMeasurement(score);
LOG("@RESULT: %46s = %10.2f %-15s", test->Name(), score, test->Unit());
try {
- test->SaveImage(((std::string)test->Name()));
+ test->SaveImage(g_out_dir.Append(FilePath(test->Name())));
} catch (std::runtime_error err) {
DEBUG("Get runtime_error while SaveImage: %s.", err.what());
}
@@ -117,7 +121,8 @@
--verbose Show verbose messages.
--vlayer Enable vulkan verification layer.
--hasty Enable hasty mode.
- --spirv_dir Path to SPIRV code for test.(default: shaders/)),")
+ --spirv_dir Path to SPIRV code for test.(default: shaders/)
+ --out_dir Path to the output directory.),")
}
bool prefixFind(std::vector<std::string> list, std::string name) {
@@ -145,7 +150,9 @@
} else if (c == 'b') {
disabled_tests = SplitString(std::string(optarg), ':');
} else if (c == 's') {
- g_spirv_dir = std::string(optarg);
+ g_spirv_dir = FilePath(optarg);
+ } else if (c == 'o') {
+ g_out_dir = FilePath(optarg);
} else if (c == '?' || c == 'h') {
PrintHelp();
return false;
diff --git a/src/testBase.h b/src/testBase.h
index 424e021..5dfc816 100644
--- a/src/testBase.h
+++ b/src/testBase.h
@@ -8,8 +8,9 @@
#include <vector>
#include <vulkan/vulkan.hpp>
-#include "utils.h"
+#include "filepath.h"
#include "vkBase.h"
+#include "utils.h"
namespace vkbench {
class testBase {
@@ -25,7 +26,7 @@
virtual double FormatMeasurement(double time) { return time; }
// SaveImage saves the rendered image to file_name.
- virtual void SaveImage(std::string file_name) const {
+ virtual void SaveImage(FilePath file_path) const {
throw std::runtime_error("Not Implemented!");
};
@@ -38,7 +39,7 @@
// Test body. Time spent will be recorded.
virtual void Run() = 0;
// Test cleanup after looping Run. Time spent here will be recorded.
- virtual void Cleanup() {};
+ virtual void Cleanup(){};
// Free and Destroy any resources allocated during Initialize. Time
// spent here will not be recorded.
virtual void Destroy() = 0;
diff --git a/src/utils.cc b/src/utils.cc
index 0aa683b..240dde3 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -14,6 +14,8 @@
#include "utils.h"
+extern FilePath g_spirv_dir;
+
const std::string kTemperatureScript =
"/usr/local/autotest/bin/temperature.py --maximum";
@@ -26,11 +28,11 @@
LOG("# DateTime: %s", buffer)
}
-std::string readFile(const std::string& filename) {
- std::ifstream file(filename, std::ios::ate | std::ios::binary);
-
+std::string readShaderFile(const std::string& filename) {
+ FilePath file_path = g_spirv_dir.Append(FilePath(filename));
+ std::ifstream file(file_path.value(), std::ios::ate | std::ios::binary);
if (!file.is_open()) {
- throw std::runtime_error("Failed to open " + filename);
+ throw std::runtime_error("Failed to open " + file_path.value());
}
DEFER(file.close());
@@ -50,14 +52,17 @@
return device.createShaderModule(create_info);
}
-// SavePPM saves data into filename in PPM format. It assumes data is in
+// SavePPM saves data into file_path in PPM format. It assumes data is in
// R8G8B8A8_UNORM format.
-void SavePPM(std::string filename,
+void SavePPM(FilePath file_path,
const char* data,
uint32_t width,
uint32_t height,
vk::DeviceSize row_pitch) {
- std::ofstream file(filename, std::ios::binary | std::ios::out);
+ FilePath directory = file_path.DirName();
+ CreateDirectory(directory);
+
+ std::ofstream file(file_path.value(), std::ios::binary | std::ios::out);
DEFER(file.close());
file << "P6\n" << width << "\n" << height << "\n" << 255 << std::endl;
@@ -110,3 +115,19 @@
return empty_value;
return !(find(list.begin(), list.end(), std::string(value)) == list.end());
}
+
+bool check_file_existence(const char* file_path, struct stat* buffer) {
+ struct stat local_buf;
+ bool exist = stat(file_path, &local_buf) == 0;
+ if (buffer && exist)
+ memcpy(buffer, &local_buf, sizeof(local_buf));
+ return exist;
+}
+
+bool check_dir_existence(const char* file_path) {
+ struct stat buffer;
+ bool exist = check_file_existence(file_path, &buffer);
+ if (!exist)
+ return false;
+ return S_ISDIR(buffer.st_mode);
+}
diff --git a/src/utils.h b/src/utils.h
index a6337d2..72453fe 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -7,17 +7,20 @@
#include <stdarg.h>
#include <stdio.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <functional>
#include <string>
#include <vector>
#include <vulkan/vulkan.hpp>
+#include "filepath.h"
+
extern int g_verbose;
void PrintDateTime();
std::vector<std::string> SplitString(const std::string& kInput, char delimiter);
-std::string readFile(const std::string& filename);
-void SavePPM(std::string, const char*, uint32_t, uint32_t, vk::DeviceSize);
+std::string readShaderFile(const std::string& filename);
+void SavePPM(FilePath, const char*, uint32_t, uint32_t, vk::DeviceSize);
vk::ShaderModule CreateShaderModule(const vk::Device& device, std::string code);
inline uint64_t GetUTime() {
@@ -29,6 +32,8 @@
bool IsItemInVector(const std::vector<std::string>& list,
const char* value,
bool empty_value);
+bool check_file_existence(const char* file_path, struct stat* buffer = NULL);
+bool check_dir_existence(const char* file_path);
inline void DbgPrintf(const char* filename,
int line,