holy shit an image

This commit is contained in:
Mars 2024-10-11 00:42:58 -04:00
parent f4fcfd3fda
commit b97a9e2b7b
Signed by untrusted user: pupbrained
GPG key ID: 874E22DF2F9DFCB5
8 changed files with 8296 additions and 52 deletions

7988
include/stb_image.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -24,16 +24,6 @@ common_cpp_args = [
add_project_arguments(cpp.get_supported_arguments(common_cpp_args), language: 'cpp')
source_file_names = [
'src/main.cpp',
]
sources = []
foreach file : source_file_names
sources += files(file)
endforeach
deps = [
dependency('fmt', include_type: 'system'),
dependency('glfw3', include_type: 'system'),
@ -43,7 +33,7 @@ deps = [
executable(
'graphics-test',
sources,
sources: files('src/main.cpp'),
include_directories: include_directories('include', is_system: true),
dependencies: deps,
)

View file

@ -1,12 +1,13 @@
#include <chrono>
#include <fmt/format.h>
#include <fstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <set>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#define VK_ENABLE_BETA_EXTENSIONS
@ -43,24 +44,26 @@ constexpr bool enableValidationLayers = true;
struct Vertex {
glm::vec2 pos;
glm::vec3 color;
glm::vec2 tex_coord;
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
return { .binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex };
}
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 2> {
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 3> {
return {
vk::VertexInputAttributeDescription { 0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, pos) },
vk::VertexInputAttributeDescription { 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) }
{ { 0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, pos) },
{ 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) },
{ 2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, tex_coord) } }
};
}
};
constexpr std::array<Vertex, 4> vertices = {
{ { { -0.5F, -0.5F }, { 1.0F, 0.0F, 0.0F } },
{ { 0.5F, -0.5F }, { 0.0F, 1.0F, 0.0F } },
{ { 0.5F, 0.5F }, { 0.0F, 0.0F, 1.0F } },
{ { -0.5F, 0.5F }, { 1.0F, 1.0F, 1.0F } } }
{ { { -0.5F, -0.5F }, { 1.0F, 0.0F, 0.0F }, { 1.0F, 0.0F } },
{ { 0.5F, -0.5F }, { 0.0F, 1.0F, 0.0F }, { 0.0F, 0.0F } },
{ { 0.5F, 0.5F }, { 0.0F, 0.0F, 1.0F }, { 0.0F, 1.0F } },
{ { -0.5F, 0.5F }, { 1.0F, 1.0F, 1.0F }, { 1.0F, 1.0F } } }
};
constexpr std::array<u16, 6> indices = { 0, 1, 2, 2, 3, 0 };
@ -102,6 +105,11 @@ class VulkanApp {
vk::UniqueCommandPool mCommandPool;
vk::UniqueImage mTextureImage;
vk::UniqueDeviceMemory mTextureImageMemory;
vk::UniqueImageView mTextureImageView;
vk::UniqueSampler mTextureSampler;
vk::UniqueBuffer mVertexBuffer;
vk::UniqueDeviceMemory mVertexBufferMemory;
vk::UniqueBuffer mIndexBuffer;
@ -190,6 +198,9 @@ class VulkanApp {
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createTextureImage();
createTextureImageView();
createTextureSampler();
createVertexBuffer();
createIndexBuffer();
createUniformBuffers();
@ -341,7 +352,9 @@ class VulkanApp {
queueCreateInfos.emplace_back(queueCreateInfo);
}
vk::PhysicalDeviceFeatures deviceFeatures;
vk::PhysicalDeviceFeatures deviceFeatures {
.samplerAnisotropy = vk::True,
};
vk::DeviceCreateInfo createInfo { .queueCreateInfoCount = static_cast<u32>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
@ -466,9 +479,19 @@ class VulkanApp {
.pImmutableSamplers = nullptr,
};
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
.binding = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
.pImmutableSamplers = nullptr,
};
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
vk::DescriptorSetLayoutCreateInfo layoutInfo {
.bindingCount = 1,
.pBindings = &uboLayoutBinding,
.bindingCount = static_cast<u32>(bindings.size()),
.pBindings = bindings.data(),
};
mDescriptorSetLayout = mDevice->createDescriptorSetLayoutUnique(layoutInfo);
@ -497,7 +520,7 @@ class VulkanApp {
fragShaderStageInfo };
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
std::array<vk::VertexInputAttributeDescription, 2> attributeDescriptions =
std::array<vk::VertexInputAttributeDescription, 3> attributeDescriptions =
Vertex::getAttributeDescriptions();
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
@ -615,6 +638,213 @@ class VulkanApp {
mCommandPool = mDevice->createCommandPoolUnique(poolInfo);
}
fn createTextureImage() -> void {
i32 texWidth = 0, texHeight = 0, texChannels = 0;
stbi_uc* pixels =
stbi_load("src/textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
vk::DeviceSize imageSize =
static_cast<vk::DeviceSize>(texWidth) * static_cast<vk::DeviceSize>(texHeight) * 4;
if (!pixels)
throw std::runtime_error("Failed to load texture image!");
vk::UniqueBuffer stagingBuffer;
vk::UniqueDeviceMemory stagingBufferMemory;
createBuffer(
imageSize,
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
stagingBuffer,
stagingBufferMemory
);
copyData(stagingBufferMemory.get(), imageSize, pixels);
stbi_image_free(pixels);
createImage(
static_cast<u32>(texWidth),
static_cast<u32>(texHeight),
vk::Format::eR8G8B8A8Srgb,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal,
mTextureImage,
mTextureImageMemory
);
transitionImageLayout(
mTextureImage.get(),
vk::Format::eR8G8B8A8Srgb,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal
);
copyBufferToImage(
stagingBuffer.get(), mTextureImage.get(), static_cast<u32>(texWidth), static_cast<u32>(texHeight)
);
transitionImageLayout(
mTextureImage.get(),
vk::Format::eR8G8B8A8Srgb,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal
);
}
fn createTextureImageView() -> void {
mTextureImageView =
createImageView(mTextureImage.get(), vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor);
}
fn createTextureSampler() -> void {
vk::PhysicalDeviceProperties properties = mPhysicalDevice.getProperties();
vk::SamplerCreateInfo samplerInfo {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.0F,
.anisotropyEnable = vk::False,
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
.compareEnable = vk::False,
.compareOp = vk::CompareOp::eAlways,
.minLod = 0.0F,
.maxLod = 0.0F,
.borderColor = vk::BorderColor::eIntOpaqueBlack,
.unnormalizedCoordinates = vk::False,
};
mTextureSampler = mDevice->createSamplerUnique(samplerInfo);
}
fn createImageView(vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags)
-> vk::UniqueImageView {
vk::ImageViewCreateInfo viewInfo {
.image = image, .viewType = vk::ImageViewType::e2D, .format = format, .subresourceRange = {
.aspectMask = aspectFlags,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
}
};
return mDevice->createImageViewUnique(viewInfo);
}
fn createImage(
u32 width,
u32 height,
vk::Format format,
vk::ImageTiling tiling,
vk::ImageUsageFlags usage,
vk::MemoryPropertyFlags properties,
vk::UniqueImage& image,
vk::UniqueDeviceMemory& imageMemory
) -> void {
vk::ImageCreateInfo imageInfo {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = { .width = width, .height = height, .depth = 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = tiling,
.usage = usage,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
image = mDevice->createImageUnique(imageInfo);
vk::MemoryRequirements memRequirements = mDevice->getImageMemoryRequirements(image.get());
vk::MemoryAllocateInfo allocInfo {
.allocationSize = memRequirements.size,
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties),
};
imageMemory = mDevice->allocateMemoryUnique(allocInfo);
mDevice->bindImageMemory(image.get(), imageMemory.get(), 0);
}
fn transitionImageLayout(
vk::Image image,
vk::Format format,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout
) -> void {
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
vk::ImageMemoryBarrier barrier {
.srcAccessMask = { },
.dstAccessMask = { },
.oldLayout = oldLayout,
.newLayout = newLayout,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = image,
// clang-format off
.subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1 },
// clang-format on
};
vk::PipelineStageFlags sourceStage;
vk::PipelineStageFlags destinationStage;
if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) {
barrier.srcAccessMask = {};
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
destinationStage = vk::PipelineStageFlagBits::eTransfer;
} else if (oldLayout == vk::ImageLayout::eTransferDstOptimal &&
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
sourceStage = vk::PipelineStageFlagBits::eTransfer;
destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
} else {
throw std::invalid_argument("Unsupported layout transition!");
}
commandBuffer->pipelineBarrier(sourceStage, destinationStage, {}, {}, {}, barrier);
endSingleTimeCommands(std::move(commandBuffer));
}
fn copyBufferToImage(vk::Buffer buffer, vk::Image image, u32 width, u32 height) -> void {
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
vk::BufferImageCopy region {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1 },
.imageOffset = { 0, 0, 0 },
.imageExtent = { width, height, 1 },
};
commandBuffer->copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, &region);
endSingleTimeCommands(std::move(commandBuffer));
}
fn createVertexBuffer() -> void {
vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
@ -629,9 +859,7 @@ class VulkanApp {
stagingBufferMemory
);
void* data = mDevice->mapMemory(stagingBufferMemory.get(), 0, bufferSize);
memcpy(data, vertices.data(), static_cast<usize>(bufferSize));
mDevice->unmapMemory(stagingBufferMemory.get());
copyData(stagingBufferMemory.get(), bufferSize, vertices.data());
createBuffer(
bufferSize,
@ -661,9 +889,7 @@ class VulkanApp {
stagingBufferMemory
);
void* data = mDevice->mapMemory(stagingBufferMemory.get(), 0, bufferSize);
memcpy(data, indices.data(), static_cast<usize>(bufferSize));
mDevice->unmapMemory(stagingBufferMemory.get());
copyData(stagingBufferMemory.get(), bufferSize, indices.data());
createBuffer(
bufferSize,
@ -700,15 +926,15 @@ class VulkanApp {
}
fn createDescriptorPool() -> void {
vk::DescriptorPoolSize poolSize {
.type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = static_cast<u32>(MAX_FRAMES_IN_FLIGHT),
std::array<vk::DescriptorPoolSize, 2> poolSizes = {
{ { .type = vk::DescriptorType::eUniformBuffer, .descriptorCount = MAX_FRAMES_IN_FLIGHT },
{ .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = MAX_FRAMES_IN_FLIGHT } },
};
vk::DescriptorPoolCreateInfo poolInfo {
.maxSets = static_cast<u32>(MAX_FRAMES_IN_FLIGHT),
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = MAX_FRAMES_IN_FLIGHT,
.poolSizeCount = static_cast<u32>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
};
mDescriptorPool = mDevice->createDescriptorPoolUnique(poolInfo);
@ -732,16 +958,31 @@ class VulkanApp {
.range = sizeof(UniformBufferObject),
};
vk::WriteDescriptorSet descriptorWrite {
vk::DescriptorImageInfo imageInfo {
.sampler = mTextureSampler.get(),
.imageView = mTextureImageView.get(),
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
std::array<vk::WriteDescriptorSet, 2> descriptorWrites = {
{ {
.dstSet = mDescriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &bufferInfo,
}, {
.dstSet = mDescriptorSets[i],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &imageInfo,
} }
};
mDevice->updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
mDevice->updateDescriptorSets(descriptorWrites, {});
}
}
@ -772,7 +1013,7 @@ class VulkanApp {
mDevice->bindBufferMemory(buffer.get(), bufferMemory.get(), 0);
}
fn copyBuffer(vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize deviceSize) -> void {
fn beginSingleTimeCommands() -> vk::UniqueCommandBuffer {
vk::CommandBufferAllocateInfo allocInfo {
.commandPool = mCommandPool.get(),
.level = vk::CommandBufferLevel::ePrimary,
@ -785,10 +1026,10 @@ class VulkanApp {
commandBuffer->begin(beginInfo);
vk::BufferCopy copyRegion { .size = deviceSize };
commandBuffer->copyBuffer(srcBuffer, dstBuffer, 1, &copyRegion);
return commandBuffer;
}
fn endSingleTimeCommands(vk::UniqueCommandBuffer commandBuffer) -> void {
commandBuffer->end();
vk::SubmitInfo submitInfo { .commandBufferCount = 1, .pCommandBuffers = &commandBuffer.get() };
@ -797,6 +1038,22 @@ class VulkanApp {
mGraphicsQueue.waitIdle();
}
fn copyData(vk::DeviceMemory stagingBufferMemory, vk::DeviceSize bufferSize, const void* src) -> void {
void* data = mDevice->mapMemory(stagingBufferMemory, 0, bufferSize);
memcpy(data, src, static_cast<usize>(bufferSize));
mDevice->unmapMemory(stagingBufferMemory);
}
fn copyBuffer(vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize deviceSize) -> void {
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
vk::BufferCopy copyRegion { .size = deviceSize };
commandBuffer->copyBuffer(srcBuffer, dstBuffer, 1, &copyRegion);
endSingleTimeCommands(std::move(commandBuffer));
}
fn findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) -> u32 {
vk::PhysicalDeviceMemoryProperties memProperties = mPhysicalDevice.getMemoryProperties();
@ -1052,7 +1309,10 @@ class VulkanApp {
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty();
}
return qfIndices.isComplete() && extensionsSupported && swapChainAdequate;
vk::PhysicalDeviceFeatures supportedFeatures = device.getFeatures();
return qfIndices.isComplete() && extensionsSupported && swapChainAdequate &&
supportedFeatures.samplerAnisotropy;
}
static fn checkDeviceExtensionSupport(vk::PhysicalDevice device) -> bool {

Binary file not shown.

View file

@ -1,9 +1,12 @@
#version 450
layout(binding = 1) uniform sampler2D texSampler;
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
outColor = texture(texSampler, fragTexCoord);
}

View file

@ -8,10 +8,13 @@ layout(binding = 0) uniform UniformBufferObject {
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}

Binary file not shown.

BIN
src/textures/texture.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB