Texture creation and binding works!

This commit is contained in:
connellpaxton 2024-01-28 12:30:42 -05:00
parent 4012425d35
commit ef1bceb388
18 changed files with 202 additions and 26 deletions

View File

@ -2,7 +2,7 @@
#include <Memory/Memory.hpp>
Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image _image, vk::MemoryPropertyFlags memory_flags) : dev(dev), image(_image) {
Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image _image, vk::MemoryPropertyFlags memory_flags, vk::Extent3D& extent) : dev(dev), image(_image) {
auto reqs = dev.getImageMemoryRequirements(image);
@ -12,13 +12,14 @@ Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image _image, vk::
};
memory = dev.allocateMemory(alloc);
dev.bindImageMemory(image, memory, 0);
this->extent = extent;
Log::debug("Image memory: %p\n", memory);
}
Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::ImageCreateInfo info, vk::MemoryPropertyFlags memory_flags) : dev(dev) {
image = dev.createImage(info);
*this = Image(phys_dev, dev, image, memory_flags);
*this = Image(phys_dev, dev, image, memory_flags, info.extent);
}
Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Extent3D extent, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags memory_flags) : dev(dev) {

View File

@ -12,9 +12,13 @@ struct Image {
vk::Image image;
vk::DeviceMemory memory;
Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image image, vk::MemoryPropertyFlags memory_flags);
Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image image, vk::MemoryPropertyFlags memory_flags, vk::Extent3D& extent);
Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::ImageCreateInfo info, vk::MemoryPropertyFlags memory_flags);
Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Extent3D extent, vk::Format format, vk::ImageTiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags memory_flags);
operator vk::Image&() {
return image;
}
void cleanup();
};

View File

@ -4,5 +4,6 @@
- Add more comments
## Long Term Improvements
- ~~Properly query surface to find supported formats for surfaces~~
- Fix all this cleanup vs destructor NONSENSE (inconsistency)
- Add pipeline caching
- Make more robust solution to window minimization (flushing out Input system should provide candidates)

View File

@ -8,6 +8,7 @@
#include <util/log.hpp>
CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
Log::debug("Command Buffer created\n");
/* (For now) allow command buffers to be individually recycled */
auto pool_info = vk::CommandPoolCreateInfo {
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer
@ -49,7 +50,7 @@ void CommandBuffer::copy(Buffer& src, Image& dst, vk::ImageLayout layout) {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 0,
.layerCount = 1,
},
.imageOffset = { 0, 0, 0 },
.imageExtent = dst.extent,

View File

@ -3,6 +3,7 @@
#include <Renderer/RenderPass.hpp>
#include <Renderer/UniformBuffer.hpp>
#include <Renderer/VertexBuffer.hpp>
#include <Resources/Texture.hpp>
#include <map>
@ -188,6 +189,22 @@ void GraphicsPipeline::update(uint32_t binding, const UniformBuffer& uni) {
}, nullptr);
}
void GraphicsPipeline::update(uint32_t binding, const Texture& tex) {
auto tex_info = vk::DescriptorImageInfo {
.sampler = tex.sampler,
.imageView = tex.view,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
dev.updateDescriptorSets(vk::WriteDescriptorSet {
.dstSet = desc_set,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &tex_info,
}, nullptr);
}
GraphicsPipeline::~GraphicsPipeline() {
dev.destroyDescriptorSetLayout(desc_layout);
dev.destroyPipelineLayout(layout);

View File

@ -7,6 +7,7 @@ struct Shader;
struct UniformBuffer;
struct VertexBuffer;
struct RenderPass;
struct Texture;
struct GraphicsPipeline {
GraphicsPipeline(vk::Device dev, const std::vector<Shader>& shaders,
@ -26,6 +27,7 @@ struct GraphicsPipeline {
/* create overload for every type of object we need to update */
void update(uint32_t binding, const UniformBuffer& uni);
void update(uint32_t binding, const Texture& tex);
~GraphicsPipeline();
};

View File

@ -1,4 +1,6 @@
#include <Renderer/Renderer.hpp>
#include <Resources/Texture.hpp>
#include <Window/Window.hpp>
#include <util/log.hpp>
@ -16,12 +18,12 @@
const static std::vector<Vertex> triangle = {
{{ 1.0, -1.0, 1.0 }, { 1.0, 0.0 }},
{{ 1.0, 1.0, 1.0 }, { 1.0, 1.0 }},
{{-1.0, 1.0, 1.0 }, { 0.0, 1.0 }},
{{-1.0, 1.0, 1.0 }, { 0.0, 1.0 }},
{{-1.0, -1.0, 1.0 }, { 0.0, 0.0 }},
{{ 1.0, -1.0, 1.0 }, { 1.0, 0.0 }},
{{ 1.0, -1.0, -1.0 }, { 1.0, 0.0 }},
{{ 1.0, 1.0, -1.0 }, { 1.0, 1.0 }},
{{-1.0, 1.0, -1.0 }, { 0.0, 1.0 }},
{{-1.0, 1.0, -1.0 }, { 0.0, 1.0 }},
{{-1.0, -1.0, -1.0 }, { 0.0, 0.0 }},
{{ 1.0, -1.0, -1.0 }, { 1.0, 0.0 }},
};
using namespace std::string_literals;
@ -101,7 +103,7 @@ Renderer::Renderer(Window& win) : win(win) {
if (discrete_idx == -1)
discrete_idx = 0;
auto& phys_dev = phys_devs[discrete_idx];
phys_dev = phys_devs[discrete_idx];
Log::info("Selected device: \"%s\" (#%zu)\n", phys_dev.getProperties().deviceName.data(), discrete_idx);
/* find queue family */
@ -184,23 +186,65 @@ Renderer::Renderer(Window& win) : win(win) {
vertex_buffer->upload(triangle);
textures = createTextures({
"assets/textures/oil.jpg",
});
std::vector<Shader> shaders = {
{ dev, "assets/shaders/basic.vert.spv", vk::ShaderStageFlagBits::eVertex },
{ dev, "assets/shaders/trace.frag.spv", vk::ShaderStageFlagBits::eFragment },
{ dev, "assets/shaders/basic.frag.spv", vk::ShaderStageFlagBits::eFragment },
};
std::vector<vk::DescriptorSetLayoutBinding> bindings = {
uniform_buffer->binding(0),
textures[0].binding(1),
};
pipeline = std::make_unique<GraphicsPipeline>(dev, shaders, swapchain->extent, *render_pass, bindings, *vertex_buffer);
pipeline->update(0, *uniform_buffer);
pipeline->update(1, textures[0]);
shaders[0].cleanup();
shaders[1].cleanup();
}
std::vector<Texture> Renderer::createTextures(const std::vector<std::string>& names) {
std::vector<Texture> ret;
CommandBuffer texture_cmd(dev, queue_family);
texture_cmd.begin();
for (const auto& name : names) {
ret.push_back({phys_dev, dev, texture_cmd, "assets/textures/oil.jpg"});
}
texture_cmd.end();
auto texture_creation_fence = dev.createFence(vk::FenceCreateInfo {});
dev.resetFences(texture_creation_fence);
queue.submit(vk::SubmitInfo {
.commandBufferCount = 1,
.pCommandBuffers = texture_cmd,
}, texture_creation_fence);
if (dev.waitForFences(texture_creation_fence, vk::True, UINT64_MAX) != vk::Result::eSuccess) {
Log::error("Failed to create textures\n");
}
dev.destroyFence(texture_creation_fence);
for (auto& tex : ret) {
tex.finishCreation();
}
texture_cmd.cleanup(dev);
return ret;
}
void Renderer::draw() {
if(dev.waitForFences(render_fence, true, UINT64_MAX) != vk::Result::eSuccess) {
@ -270,7 +314,7 @@ void Renderer::draw() {
const auto p = glm::perspective(glm::radians(90.0f), static_cast<float>(sz.width) / static_cast<float>(sz.height), 0.01f, 50.0f);
uniform_buffer->upload(UniformData{
//.mvp = p * glm::rotate(glm::mat4(1.0), glm::radians(static_cast<float>(frame)), glm::vec3(1.0, 1.0, 1.0)),
.mvp = p * glm::rotate(glm::mat4(1.0), glm::radians(static_cast<float>(frame)), glm::vec3(1.0, 1.0, 1.0)),
.time = static_cast<float>(frame) * 0.0167f,
.aspect_ratio = static_cast<float>(sz.width)/static_cast<float>(sz.height),
});
@ -331,6 +375,10 @@ Renderer::~Renderer() {
vertex_buffer.reset();
pipeline.reset();
for (auto& tex : textures) {
tex.cleanup();
}
swapchain.reset();
dev.destroySemaphore(image_wait_semaphore);

View File

@ -12,18 +12,22 @@
struct Window;
struct UniformBuffer;
struct VertexBuffer;
struct Texture;
/* Contains all of the Vulkan objects involved in rendering the scene */
struct Renderer {
Renderer(Window& win);
~Renderer();
std::vector<Texture> createTextures(const std::vector<std::string>& names);
void draw();
void present();
Window& win;
vk::Instance instance;
vk::PhysicalDevice phys_dev;
vk::Device dev;
vk::Fence render_fence;
@ -42,6 +46,8 @@ struct Renderer {
std::unique_ptr<VertexBuffer> vertex_buffer;
std::unique_ptr<UniformBuffer> uniform_buffer;
std::vector<Texture> textures;
uint32_t current_image_idx;
uint64_t frame = 0;
};

View File

@ -5,32 +5,103 @@
#include <Renderer/CommandBuffer.hpp>
#include <util/file.hpp>
#include <util/log.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
/* externall synchonized, just writes to a command buffer, you still need to pull the trigger (allows bulk texture image preperation) */
Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname) : dev(dev) {
Log::debug("Starting texture creation for: %s\n", fname.c_str());
Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname) {
int n_channels;
vk::Extent3D extent;
auto image_data = stbi_load(fname.c_str(), reinterpret_cast<int*>(&extent.width), reinterpret_cast<int*>(&extent.height), &n_channels, STBI_rgb_alpha);
extent.depth = 1;
image = std::make_unique<Image>(phys_dev, dev, extent, vk::Format::eR8G8B8A8Srgb,
vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled,
image = std::make_unique<Image>(phys_dev, dev, extent, vk::Format::eR8G8B8A8Unorm,
vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
vk::DeviceSize sz = static_cast<vk::DeviceSize>(extent.width * extent.height) * sizeof(uint32_t);
/* staging buffer to hold image data from the CPU */
Buffer staging(phys_dev, dev, sz, vk::BufferUsageFlagBits::eTransferSrc,
staging = std::make_unique<Buffer>(phys_dev, dev, sz, vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
staging.upload(image_data);
staging->upload(image_data);
stbi_image_free(image_data);
command_buffer.copy(staging, *image);
/* pipeline memory barrier ensures this doesn't get reordered wrong */
/* TODO: Finish, also rethink if we want submission to happen internally or externally to the function for creation */
auto staging_buffer_trans_barrier = vk::ImageMemoryBarrier {
.srcAccessMask = vk::AccessFlagBits(0),
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.image = *image,
.subresourceRange = vk::ImageSubresourceRange {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.levelCount = 1,
.layerCount = 1,
},
};
/* transfer optimal --> shader optimal */
auto shader_barrier = vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.image = *image,
.subresourceRange = vk::ImageSubresourceRange {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.levelCount = 1,
.layerCount = 1,
},
};
command_buffer.command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits(0), nullptr, nullptr, staging_buffer_trans_barrier);
command_buffer.copy(*staging, *image);
command_buffer.command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, vk::DependencyFlagBits(0), nullptr, nullptr, shader_barrier);
}
void Texture::finishCreation() {
view = dev.createImageView(vk::ImageViewCreateInfo {
.image = *image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eR8G8B8A8Unorm,
.subresourceRange = vk::ImageSubresourceRange{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.levelCount = 1,
.layerCount = 1,
},
});
sampler = dev.createSampler(vk::SamplerCreateInfo {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
.addressModeW = vk::SamplerAddressMode::eClampToEdge,
.mipLodBias = 0.0,
.anisotropyEnable = vk::False,
.maxAnisotropy = 0.0,
.compareEnable = vk::False,
.compareOp = vk::CompareOp::eAlways,
.minLod = 0.0,
.maxLod = 0.0,
.borderColor = vk::BorderColor::eIntOpaqueBlack,
.unnormalizedCoordinates = vk::False,
});
}
void Texture::cleanup() {
staging.reset();
image->cleanup();
dev.destroyImageView(view);
dev.destroySampler(sampler);
}

View File

@ -7,8 +7,31 @@
#include <memory>
struct CommandBuffer;
struct Buffer;
struct Texture {
vk::Device dev;
std::unique_ptr<Image> image;
Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname);
vk::ImageView view;
vk::Sampler sampler;
std::unique_ptr<Buffer> staging;
Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname);
inline vk::DescriptorSetLayoutBinding binding(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eAll) {
return vk::DescriptorSetLayoutBinding {
.binding = binding,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
.pImmutableSamplers = nullptr,
};
}
/* seperates out steps that need to be taken *after* command buffer is submitted */
void finishCreation();
void cleanup();
};

View File

@ -8,6 +8,8 @@ layout (set = 0, binding = 0) uniform Matrices {
float time;
};
layout (set = 0, binding = 1) uniform sampler2D tex;
void main() {
FragColor = vec4(1.0, 1.0, 1.0, 1.0);
FragColor = texture(tex, texCoord);
}

Binary file not shown.

View File

@ -10,6 +10,6 @@ layout (set = 0, binding = 0) uniform Matrices {
};
void main() {
gl_Position = vec4(aPos, 1.0);
gl_Position = mpv*vec4(aPos, 1.0);
texCoord = aTexCoord;
}

Binary file not shown.

Binary file not shown.

BIN
assets/textures/oil.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -42,7 +42,7 @@ int main(int argc, char* argv[]) {
ren.draw();
ren.present();
Log::debug("Frame: %.2lf milliseconds (60fps ~ 16.67)\n", frame_timer.stop());
Log::debug("Frame: %.2lf milliseconds (60fps ~ 16.67)\r", frame_timer.stop());
}
} catch (const std::string& e) {

View File

@ -13,7 +13,7 @@ namespace Log {
return static_cast<MessageLevelBit>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
}
static const MessageLevelBit log_mask = ERROR | INFO;
static const MessageLevelBit log_mask = ERROR | INFO | DEBUG;
template<typename ...Args>
static void print(MessageLevelBit level, const std::string& fmt, Args... args) {