blob: 3e3a64a9678b4610a6b2cec8204e574e6aa5540e [file] [log] [blame]
// Copyright 2019 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 <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include "src/utils.h"
extern FilePath g_spirv_dir;
vkbench::Image::Image(const std::string& file_name) {
load(g_spirv_dir.Append(FilePath(file_name)));
}
void vkbench::Image::Save(FilePath file_path) {
if (data_ == nullptr) {
RUNTIME_ERROR("No data to save");
}
std::string ext = "";
std::string::size_type last_dot =
file_path.value().find_last_of(".", std::string::npos);
if (last_dot == std::string::npos) {
RUNTIME_ERROR("No image extension found for file: {}", file_path);
}
ext = file_path.value().substr(last_dot + 1);
if (ext == "png") {
savePNG(file_path);
} else if (ext == "ppm") {
savePPM(file_path);
} else {
RUNTIME_ERROR("Unknown extension: {}", ext);
}
DEBUG("Image saved to {}", file_path);
// Try to flush saved image to disk such that more data survives a hard crash.
system("/bin/sync");
}
void vkbench::Image::load(FilePath file_path) {
std::string ext = "";
std::string::size_type last_dot =
file_path.value().find_last_of(".", std::string::npos);
if (last_dot == std::string::npos) {
RUNTIME_ERROR("No image extension found for file: {}", file_path);
}
ext = file_path.value().substr(last_dot + 1);
if (ext == "png") {
loadPNG(file_path);
} else {
NOT_SUPPORT("Unknown extension: {}", ext);
}
}
void vkbench::Image::savePPM(FilePath file_path) {
FilePath directory = file_path.DirName();
CreateDirectory(directory);
std::ofstream file(file_path.value(), std::ios::binary | std::ios::out);
DEFER(file.close());
const unsigned char* row_ptr = data_ + resource_layout_.offset;
file << "P6\n"
<< extent_.width << "\n"
<< extent_.height << "\n"
<< 255 << std::endl;
for (uint32_t y = 0; y < extent_.height; y++) {
unsigned int* row = (unsigned int*)row_ptr;
for (uint32_t x = 0; x < extent_.width; x++) {
file.write(reinterpret_cast<char*>(row), 3);
row++;
}
row_ptr += resource_layout_.rowPitch;
}
}
void vkbench::Image::savePNG(FilePath file_path) {
FilePath directory = file_path.DirName();
CreateDirectory(directory);
/* create file */
FILE* file = fopen(file_path.c_str(), "wb");
if (!file)
RUNTIME_ERROR("File {} couldn't be opened for writing", file_path);
DEFER(fclose(file));
png_bytep* row_pointers = new png_bytep[sizeof(png_bytep) * extent_.height];
unsigned char* row_ptr = data_ + resource_layout_.offset;
for (int y = extent_.height - 1; y >= 0; y--) {
row_pointers[y] = new png_byte[4 * extent_.width];
memcpy(row_pointers[y], row_ptr, 4 * extent_.width);
row_ptr += resource_layout_.rowPitch;
}
/* initialize stuff */
png_structp png_ptr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
RUNTIME_ERROR("png_create_write_struct failed");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
RUNTIME_ERROR("png_create_info_struct failed");
if (setjmp(png_jmpbuf(png_ptr)))
RUNTIME_ERROR("error during png_init_io");
png_init_io(png_ptr, file);
/* write header */
if (setjmp(png_jmpbuf(png_ptr)))
RUNTIME_ERROR("error during writing png header");
png_byte bit_depth = 8; // 8 bits per channel RGBA
png_byte color_type = PNG_COLOR_TYPE_RGBA; // RGBA
png_set_IHDR(png_ptr, info_ptr, extent_.width, extent_.height, bit_depth,
color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
/* write bytes */
if (setjmp(png_jmpbuf(png_ptr)))
RUNTIME_ERROR("error during png_write_image");
png_write_image(png_ptr, row_pointers);
/* end write */
if (setjmp(png_jmpbuf(png_ptr)))
RUNTIME_ERROR("error during png_write_end");
png_write_end(png_ptr, NULL);
for (int y = 0; y < extent_.height; y++)
delete row_pointers[y];
delete[] row_pointers;
}
void vkbench::Image::loadPNG(FilePath file_path) {
FILE* fp = fopen(file_path.c_str(), "rb");
if (!fp) {
RUNTIME_ERROR("File {} could not be oppened", file_path.value());
}
DEFER(fclose(fp));
unsigned char header[8];
fread(header, 1, 8, fp);
if (png_sig_cmp(header, 0, 8)) {
RUNTIME_ERROR("Faile {} is not recognized as a PNG file",
file_path.value());
}
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
RUNTIME_ERROR("png_create_read_struct fails");
}
png_infop info = png_create_info_struct(png);
if (!info) {
RUNTIME_ERROR("png_create_info_struct fails");
}
if (setjmp(png_jmpbuf(png))) {
RUNTIME_ERROR("loadPNG fails to setjmp");
}
png_init_io(png, fp);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
extent_.width = png_get_image_width(png, info);
extent_.height = png_get_image_height(png, info);
resource_layout_.offset = 0;
resource_layout_.rowPitch = png_get_rowbytes(png, info);
resource_layout_.size = extent_.height * resource_layout_.rowPitch;
png_byte color_type = png_get_color_type(png, info);
if (color_type != PNG_COLOR_TYPE_RGBA) {
RUNTIME_ERROR("Not recognized png color type: {}", color_type);
}
png_byte bit_depth = png_get_bit_depth(png, info);
if (bit_depth != 8) {
RUNTIME_ERROR("Not supported bit_depth: {}", bit_depth);
}
// read file
if (setjmp(png_jmpbuf(png))) {
RUNTIME_ERROR("PNG read image fails");
}
data_ = new unsigned char[extent_.height * resource_layout_.rowPitch];
png_bytep *row_ptrs = new png_bytep[extent_.height];
DEFER(delete row_ptrs);
for (int y = 0; y < extent_.height; y++) {
row_ptrs[y] = data_ + (extent_.height - 1 - y) * resource_layout_.rowPitch;
}
png_read_image(png, row_ptrs);
}
void PrintDateTime() {
time_t timer;
char buffer[26];
time(&timer);
struct tm tm_info;
localtime_r(&timer, &tm_info);
strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", &tm_info);
LOG("# DateTime: {}", buffer);
}
std::string readShaderFile(const std::string& filename) {
FilePath file_path = g_spirv_dir.Append(FilePath(filename + ".spv"));
std::ifstream file(file_path.value(), std::ios::ate | std::ios::binary);
if (!file.is_open()) {
RUNTIME_ERROR("Failed to open {}", file_path);
}
DEFER(file.close());
size_t fileSize = (size_t)file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
return std::string(buffer.begin(), buffer.end());
}
// CreateShaderModule creates a shader module from a loaded SPIR-V code.
vk::ShaderModule CreateShaderModule(const vk::Device& device,
std::string code) {
vk::ShaderModuleCreateInfo create_info;
create_info.setCodeSize(code.length());
create_info.setPCode(reinterpret_cast<const uint32_t*>(code.c_str()));
return device.createShaderModule(create_info);
}
/* Execute a shell command and return its file descriptor for reading output. */
/* @param command: command to be run. */
/* @param result: the stdout of the command output. */
/* @return true if the command is executed successfully. */
bool ExecuteCommand(const std::string& kCommand,
std::string* result = nullptr) {
FILE* fd = popen(kCommand.c_str(), "r");
if (!fd) {
return false;
}
if (result) {
fseek(fd, 0, SEEK_END);
int64_t size = ftell(fd);
fseek(fd, 0, SEEK_SET);
fread(&result[0], sizeof(char), size, fd);
}
return pclose(fd) == 0;
}
std::vector<std::string> SplitString(const std::string& kInput,
char delimiter) {
std::vector<std::string> result;
std::stringstream srcStream(kInput);
std::string token;
while (getline(srcStream, token, delimiter)) {
result.push_back(token);
}
return result;
}
bool IsItemInVector(const std::vector<std::string>& list,
const char* value,
bool empty_value = false) {
if (list.empty())
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);
}
uint32_t randi(uint32_t max) {
return rand() % (max + 1);
}