-
Notifications
You must be signed in to change notification settings - Fork 1
/
Texture.cpp
129 lines (97 loc) · 5.64 KB
/
Texture.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "Texture.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <iostream>
#include "Buffer.h"
Texture::Texture(std::shared_ptr<Instance> inst, vk::Format format, vk::ImageUsageFlags flags, glm::ivec2 sz, uint32_t levels) : instance(std::move(inst)), _format(format), _size(sz), _flags(flags) {
if (levels == TEXTURE_LEVELS_AUTO) {
int width = _size.x, height = _size.y;
levels = 1;
while(width > 1 || height > 1) {
width = glm::max(width / 2, 1);
height = glm::max(height / 2, 1);
levels++;
}
}
_levels = levels;
auto queue_families = std::array<uint32_t, 2>{ instance->graphics_queue_index(), instance->compute_queue_index() };
vk::ImageCreateInfo imageInfo( {}, vk::ImageType::e2D, _format, { (uint32_t) _size.x, (uint32_t) _size.y, 1 }, _levels, 1, vk::SampleCountFlagBits::e1, vk::ImageTiling::eOptimal, flags, vk::SharingMode::eExclusive, queue_families);
_image = instance->device().createImage(imageInfo);
auto memoryRequirements = instance->device().getImageMemoryRequirements(_image);
auto any = false;
auto memoryIndex = 0;
auto index = 0;
for(auto& t : instance->memory_properties().memoryTypes) {
if ((memoryRequirements.memoryTypeBits & (1 << index)) == 0) {
continue;
}
if ((t.propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal) == vk::MemoryPropertyFlagBits::eDeviceLocal &&
(instance->memory_properties().memoryHeaps[t.heapIndex].flags & vk::MemoryHeapFlagBits::eDeviceLocal) == vk::MemoryHeapFlagBits::eDeviceLocal) {
memoryIndex = index;
any = true;
break;
}
index++;
}
if(!any) {
throw std::runtime_error("Failed to find adequate memory type for texture");
}
vk::MemoryAllocateInfo memoryInfo(memoryRequirements.size, memoryIndex);
_memory = instance->device().allocateMemory(memoryInfo);
instance->device().bindImageMemory(_image, _memory, 0);
}
Texture::~Texture() {
instance->device().destroyImage(_image);
instance->device().freeMemory(_memory);
}
bool Texture::fill_from_file(const vk::CommandBuffer& cmd, const std::filesystem::path &path, uint32_t level, vk::ImageLayout targetLayout) {
auto targetWidth = _size.x, targetHeight = _size.y;
for(int i = 0; i < level; i++) {
targetWidth = glm::max(targetWidth / 2, 1);
targetHeight = glm::max(targetHeight / 2, 1);
}
int fileWidth = 0, fileHeight = 0, fileChannels = 0;
stbi_uc* pixels = stbi_load(path.string().c_str(), &fileWidth, &fileHeight, &fileChannels, STBI_rgb_alpha);
if (pixels == nullptr) {
return false;
}
auto tmpBuffer = Buffer(instance, fileWidth * fileHeight * fileChannels, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
{
auto map = tmpBuffer.map();
std::memcpy(static_cast<void*>(map), (void*)pixels, tmpBuffer.size());
}
stbi_image_free(pixels);
vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, level, 1, 0, 1);
vk::ImageMemoryBarrier transfer_barrier(vk::AccessFlagBits::eNone, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, {}, {}, _image, subResourceRange);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, nullptr, nullptr, transfer_barrier);
vk::BufferImageCopy imageCopy(0, 0, 0, { vk::ImageAspectFlagBits::eColor, level, 0, 1}, { fileWidth, fileHeight, 1});
cmd.copyBufferToImage(static_cast<vk::Buffer>(tmpBuffer), _image, vk::ImageLayout::eTransferDstOptimal, imageCopy);
vk::ImageMemoryBarrier readable_barrier(vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eShaderRead, vk::ImageLayout::eTransferDstOptimal, targetLayout, {}, {}, _image, subResourceRange);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, readable_barrier);
return true;
}
bool Texture::fill_from_data(const vk::CommandBuffer &cmd, const std::vector<uint8_t> &data, uint32_t level,
uint32_t channels, vk::ImageLayout targetLayout) {
level = glm::max(level, 1U);
auto targetWidth = _size.x, targetHeight = _size.y;
for(int i = 0; i < level; i++) {
targetWidth = glm::max(targetWidth / 2, 1);
targetHeight = glm::max(targetHeight / 2, 1);
}
if (targetWidth * targetHeight * channels > data.size()) {
return false;
}
auto buffer = Buffer(instance, targetWidth * targetHeight * channels, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
{
auto map = buffer.map();
std::memcpy(static_cast<void*>(map), data.data(), buffer.size());
}
vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, level, 1, 0, 1);
vk::ImageMemoryBarrier transfer_barrier(vk::AccessFlagBits::eNone, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, {}, {}, _image, subResourceRange);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, nullptr, nullptr, transfer_barrier);
vk::BufferImageCopy imageCopy(0, 0, 0, { vk::ImageAspectFlagBits::eColor, level, 0, 1}, { targetWidth, targetHeight, 1});
cmd.copyBufferToImage(static_cast<vk::Buffer>(buffer), _image, vk::ImageLayout::eTransferDstOptimal, imageCopy);
vk::ImageMemoryBarrier readable_barrier(vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eShaderRead, vk::ImageLayout::eTransferDstOptimal, targetLayout, {}, {}, _image, subResourceRange);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, readable_barrier);
return true;
}