blob: e58fea845b31b8b72613ba0a8112c77023c7082b [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "lib/escher/util/image_utils.h"
#include <random>
#include "lib/escher/impl/gpu_uploader.h"
#include "lib/escher/impl/vulkan_utils.h"
#include "lib/escher/vk/gpu_mem.h"
#include "lib/escher/vk/image_factory.h"
#include "lib/escher/util/image_formats.h"
namespace {
struct RGBA {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
} // namespace
namespace escher {
namespace image_utils {
size_t BytesPerPixel(vk::Format format) {
switch (format) {
case vk::Format::eR8G8B8A8Unorm:
case vk::Format::eB8G8R8A8Unorm:
return 4;
case vk::Format::eR8Unorm:
return 1;
default:
FXL_CHECK(false);
return 0;
}
}
vk::Image CreateVkImage(const vk::Device& device, ImageInfo info) {
vk::ImageCreateInfo create_info;
create_info.imageType = vk::ImageType::e2D;
create_info.format = info.format;
create_info.extent = vk::Extent3D{info.width, info.height, 1};
create_info.mipLevels = 1;
create_info.arrayLayers = 1;
switch (info.sample_count) {
case 1:
create_info.samples = vk::SampleCountFlagBits::e1;
break;
case 2:
create_info.samples = vk::SampleCountFlagBits::e2;
break;
case 4:
create_info.samples = vk::SampleCountFlagBits::e4;
break;
case 8:
create_info.samples = vk::SampleCountFlagBits::e8;
break;
default:
FXL_DCHECK(false);
}
create_info.tiling = vk::ImageTiling::eOptimal;
create_info.usage = info.usage;
create_info.sharingMode = vk::SharingMode::eExclusive;
create_info.initialLayout = vk::ImageLayout::eUndefined;
vk::Image image = ESCHER_CHECKED_VK_RESULT(device.createImage(create_info));
return image;
}
ImagePtr NewDepthImage(ImageFactory* image_factory,
vk::Format format,
uint32_t width,
uint32_t height,
vk::ImageUsageFlags additional_flags) {
FXL_DCHECK(image_factory);
ImageInfo info;
info.format = format;
info.width = width;
info.height = height;
info.sample_count = 1;
info.usage =
additional_flags | vk::ImageUsageFlagBits::eDepthStencilAttachment;
return image_factory->NewImage(info);
}
ImagePtr NewColorAttachmentImage(ImageFactory* image_factory,
uint32_t width,
uint32_t height,
vk::ImageUsageFlags additional_flags) {
FXL_DCHECK(image_factory);
ImageInfo info;
info.format = vk::Format::eB8G8R8A8Unorm;
info.width = width;
info.height = height;
info.sample_count = 1;
info.usage = additional_flags | vk::ImageUsageFlagBits::eColorAttachment;
return image_factory->NewImage(info);
}
ImagePtr NewGpuImageFromPixels(ImageFactory* image_factory,
vk::Format format,
uint32_t width,
uint32_t height,
vk::ImageUsageFlags additional_flags) {
FXL_DCHECK(image_factory);
ImageInfo info;
info.format = format;
info.width = width;
info.height = height;
info.sample_count = 1;
info.usage = additional_flags | vk::ImageUsageFlagBits::eTransferDst |
vk::ImageUsageFlagBits::eSampled;
// Create the new image.
auto gpu_image = image_factory->NewImage(info);
return gpu_image;
}
void WritePixelsToImage(
impl::GpuUploader* gpu_uploader,
uint8_t* pixels,
ImagePtr gpu_image,
const escher::image_formats::ImgConvertFunc& conversion_func) {
FXL_DCHECK(gpu_uploader);
FXL_DCHECK(gpu_image);
FXL_DCHECK(pixels);
size_t bytes_per_pixel = BytesPerPixel(gpu_image->info().format);
size_t width = gpu_image->info().width;
size_t height = gpu_image->info().height;
auto writer = gpu_uploader->GetWriter(width * height * bytes_per_pixel);
if (!conversion_func) {
std::memcpy(writer.ptr(), pixels, width * height * bytes_per_pixel);
} else {
conversion_func(writer.ptr(), pixels, width, height);
}
vk::BufferImageCopy region;
region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = width;
region.imageExtent.height = height;
region.imageExtent.depth = 1;
region.bufferOffset = 0;
writer.WriteImage(gpu_image, region, Semaphore::New(gpu_uploader->device()));
writer.Submit();
}
ImagePtr NewRgbaImage(ImageFactory* image_factory,
impl::GpuUploader* gpu_uploader,
uint32_t width,
uint32_t height,
uint8_t* pixels) {
FXL_DCHECK(image_factory);
FXL_DCHECK(gpu_uploader);
auto gpu_image = NewGpuImageFromPixels(
image_factory, vk::Format::eR8G8B8A8Unorm, width, height);
WritePixelsToImage(gpu_uploader, pixels, gpu_image);
return gpu_image;
}
ImagePtr NewCheckerboardImage(ImageFactory* image_factory,
impl::GpuUploader* gpu_uploader,
uint32_t width,
uint32_t height) {
FXL_DCHECK(image_factory);
FXL_DCHECK(gpu_uploader);
auto gpu_image = NewGpuImageFromPixels(
image_factory, vk::Format::eR8G8B8A8Unorm, width, height);
auto pixels = NewCheckerboardPixels(width, height);
WritePixelsToImage(gpu_uploader, pixels.get(), gpu_image);
return gpu_image;
}
ImagePtr NewGradientImage(ImageFactory* image_factory,
impl::GpuUploader* gpu_uploader,
uint32_t width,
uint32_t height) {
FXL_DCHECK(image_factory);
FXL_DCHECK(gpu_uploader);
auto pixels = NewGradientPixels(width, height);
auto gpu_image = NewGpuImageFromPixels(
image_factory, vk::Format::eR8G8B8A8Unorm, width, height);
WritePixelsToImage(gpu_uploader, pixels.get(), gpu_image);
return gpu_image;
}
ImagePtr NewNoiseImage(ImageFactory* image_factory,
impl::GpuUploader* gpu_uploader,
uint32_t width,
uint32_t height,
vk::ImageUsageFlags additional_flags) {
FXL_DCHECK(image_factory);
FXL_DCHECK(gpu_uploader);
auto pixels = NewNoisePixels(width, height);
auto gpu_image = NewGpuImageFromPixels(image_factory, vk::Format::eR8Unorm,
width, height, additional_flags);
WritePixelsToImage(gpu_uploader, pixels.get(), gpu_image);
return gpu_image;
}
std::unique_ptr<uint8_t[]> NewCheckerboardPixels(uint32_t width,
uint32_t height,
size_t* out_size) {
FXL_DCHECK(width % 2 == 0);
FXL_DCHECK(height % 2 == 0);
size_t size_in_bytes = width * height * sizeof(RGBA);
auto ptr = std::make_unique<uint8_t[]>(size_in_bytes);
if (out_size) {
(*out_size) = size_in_bytes;
}
RGBA* pixels = reinterpret_cast<RGBA*>(ptr.get());
for (uint32_t j = 0; j < height; ++j) {
for (uint32_t i = 0; i < width; ++i) {
uint32_t index = j * width + i;
auto& p = pixels[index];
p.r = p.g = p.b = (i + j) % 2 ? 0 : 255;
p.a = 255;
}
}
return ptr;
}
std::unique_ptr<uint8_t[]> NewGradientPixels(uint32_t width,
uint32_t height,
size_t* out_size) {
FXL_DCHECK(width % 2 == 0);
FXL_DCHECK(height % 2 == 0);
size_t size_in_bytes = width * height * sizeof(RGBA);
auto ptr = std::make_unique<uint8_t[]>(size_in_bytes);
if (out_size) {
(*out_size) = size_in_bytes;
}
RGBA* pixels = reinterpret_cast<RGBA*>(ptr.get());
for (uint32_t j = 0; j < height; ++j) {
uint32_t intensity = (height - j) * 255 / height;
for (uint32_t i = 0; i < width; ++i) {
uint32_t index = j * width + i;
auto& p = pixels[index];
p.r = p.g = p.b = intensity;
p.a = 255;
}
}
return ptr;
}
std::unique_ptr<uint8_t[]> NewNoisePixels(uint32_t width,
uint32_t height,
size_t* out_size) {
size_t size_in_bytes = width * height;
auto ptr = std::make_unique<uint8_t[]>(size_in_bytes);
if (out_size) {
(*out_size) = size_in_bytes;
}
auto pixels = ptr.get();
// TODO: when we have a random source, use it.
#if defined(__Fuchsia__)
std::default_random_engine prng(12345);
#else
std::random_device seed;
std::default_random_engine prng(seed());
#endif
std::uniform_int_distribution<uint8_t> random;
for (uint32_t j = 0; j < height; ++j) {
for (uint32_t i = 0; i < width; ++i) {
pixels[j * width + i] = random(prng);
}
}
return ptr;
}
} // namespace image_utils
} // namespace escher