texture loading

This commit is contained in:
tylermurphy534 2022-09-20 22:02:58 -04:00
parent b2af1ca699
commit 249f6c9fa3
21 changed files with 397 additions and 65 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "glm"] [submodule "glm"]
path = lib/glm path = lib/glm
url = https://github.com/g-truc/glm.git url = https://github.com/g-truc/glm.git
[submodule "stb"]
path = lib/stb
url = https://github.com/nothings/stb.git

View file

@ -4,6 +4,7 @@ INCFLAGS = -Isrc
INCFLAGS += -Iengine INCFLAGS += -Iengine
INCFLAGS += -Ilib/glfw/include INCFLAGS += -Ilib/glfw/include
INCFLAGS += -Ilib/glm INCFLAGS += -Ilib/glm
INCFLAGS += -Ilib/stb
CCFLAGS = -std=c++17 -O2 -g CCFLAGS = -std=c++17 -O2 -g
CCFLAGS += $(INCFLAGS) CCFLAGS += $(INCFLAGS)

View file

@ -9,13 +9,15 @@ XeDescriptorSetLayout::Builder &XeDescriptorSetLayout::Builder::addBinding(
uint32_t binding, uint32_t binding,
VkDescriptorType descriptorType, VkDescriptorType descriptorType,
VkShaderStageFlags stageFlags, VkShaderStageFlags stageFlags,
uint32_t count) { VkSampler *sampler) {
assert(bindings.count(binding) == 0 && "Binding already in use"); assert(bindings.count(binding) == 0 && "Binding already in use");
uint32_t count = 1;
VkDescriptorSetLayoutBinding layoutBinding{}; VkDescriptorSetLayoutBinding layoutBinding{};
layoutBinding.binding = binding; layoutBinding.binding = binding;
layoutBinding.descriptorType = descriptorType; layoutBinding.descriptorType = descriptorType;
layoutBinding.descriptorCount = count; layoutBinding.descriptorCount = count;
layoutBinding.stageFlags = stageFlags; layoutBinding.stageFlags = stageFlags;
layoutBinding.pImmutableSamplers = sampler;
bindings[binding] = layoutBinding; bindings[binding] = layoutBinding;
return *this; return *this;
} }

View file

@ -19,7 +19,7 @@ class XeDescriptorSetLayout {
uint32_t binding, uint32_t binding,
VkDescriptorType descriptorType, VkDescriptorType descriptorType,
VkShaderStageFlags stageFlags, VkShaderStageFlags stageFlags,
uint32_t count = 1); VkSampler *sampler);
std::unique_ptr<XeDescriptorSetLayout> build() const; std::unique_ptr<XeDescriptorSetLayout> build() const;
private: private:

View file

@ -1,4 +1,5 @@
#include "xe_engine.hpp" #include "xe_engine.hpp"
#include "xe_image.hpp"
#include <chrono> #include <chrono>
namespace xe { namespace xe {
@ -7,18 +8,15 @@ XeEngine::XeEngine(int width, int height, std::string name) : xeWindow{width, he
xeDevice{xeWindow}, xeDevice{xeWindow},
xeRenderer{xeWindow, xeDevice}, xeRenderer{xeWindow, xeDevice},
xeCamera{} { xeCamera{} {
loadDescriptors(); loadDescriptorPool();
}; };
void XeEngine::loadDescriptors() { void XeEngine::loadDescriptorPool() {
xeDescriptorPool = XeDescriptorPool::Builder(xeDevice) xeDescriptorPool = XeDescriptorPool::Builder(xeDevice)
.setMaxSets(XeSwapChain::MAX_FRAMES_IN_FLIGHT) .setMaxSets(XeSwapChain::MAX_FRAMES_IN_FLIGHT)
.addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, XeSwapChain::MAX_FRAMES_IN_FLIGHT) .addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, XeSwapChain::MAX_FRAMES_IN_FLIGHT)
.addPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, XeSwapChain::MAX_FRAMES_IN_FLIGHT) .addPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, XeSwapChain::MAX_FRAMES_IN_FLIGHT)
.build(); .addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, XeSwapChain::MAX_FRAMES_IN_FLIGHT)
xeDescriptorSetLayout = XeDescriptorSetLayout::Builder(xeDevice)
.addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT)
.build(); .build();
} }
@ -35,6 +33,10 @@ std::shared_ptr<XeModel> XeEngine::loadModelFromData(std::vector<XeModel::Vertex
return std::make_shared<XeModel>(xeDevice, builder); return std::make_shared<XeModel>(xeDevice, builder);
} }
std::shared_ptr<XeImage> XeEngine::loadImage(const std::string &filename) {
return std::make_shared<XeImage>(xeDevice, filename);
}
bool XeEngine::poll() { bool XeEngine::poll() {
glfwPollEvents(); glfwPollEvents();
auto newTime = std::chrono::high_resolution_clock::now(); auto newTime = std::chrono::high_resolution_clock::now();

View file

@ -5,8 +5,9 @@
#include "xe_renderer.hpp" #include "xe_renderer.hpp"
#include "xe_camera.hpp" #include "xe_camera.hpp"
#include "xe_descriptors.hpp" #include "xe_descriptors.hpp"
#include <chrono> #include "xe_image.hpp"
#include <chrono>
#include <string> #include <string>
namespace xe { namespace xe {
@ -26,6 +27,7 @@ class XeEngine {
std::shared_ptr<XeModel> loadModelFromFile(const std::string &filename); std::shared_ptr<XeModel> loadModelFromFile(const std::string &filename);
std::shared_ptr<XeModel> loadModelFromData(std::vector<XeModel::Vertex> vertices, std::vector<uint32_t> indices); std::shared_ptr<XeModel> loadModelFromData(std::vector<XeModel::Vertex> vertices, std::vector<uint32_t> indices);
std::shared_ptr<XeImage> loadImage(const std::string &filename);
bool beginFrame() { return xeRenderer.beginFrame(); } bool beginFrame() { return xeRenderer.beginFrame(); }
void endFrame() { xeRenderer.endFrame(); } void endFrame() { xeRenderer.endFrame(); }
@ -36,7 +38,7 @@ class XeEngine {
private: private:
void loadDescriptors(); void loadDescriptorPool();
XeWindow xeWindow; XeWindow xeWindow;
XeDevice xeDevice; XeDevice xeDevice;
@ -49,7 +51,6 @@ class XeEngine {
float FOV = 50.f; float FOV = 50.f;
std::unique_ptr<XeDescriptorPool> xeDescriptorPool; std::unique_ptr<XeDescriptorPool> xeDescriptorPool;
std::unique_ptr<XeDescriptorSetLayout> xeDescriptorSetLayout;
friend class XeRenderSystem; friend class XeRenderSystem;
}; };

166
engine/xe_image.cpp Normal file
View file

@ -0,0 +1,166 @@
#include "xe_image.hpp"
#include <vulkan/vulkan.h>
#include <stdexcept>
#include <memory>
#include <cstring>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
namespace xe {
XeImage::XeImage(XeDevice &xeDevice, const std::string &filename) : xeDevice{xeDevice} {
createTextureImage(filename);
}
XeImage::~XeImage() {
vkDestroyImage(xeDevice.device(), textureImage, nullptr);
vkFreeMemory(xeDevice.device(), textureImageMemory, nullptr);
}
void XeImage::createTextureImage(const std::string &filename) {
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load(filename.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = texWidth * texHeight * 4;
if (!pixels) {
throw std::runtime_error("failed to load texture: " + filename);
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
xeDevice.createBuffer(
imageSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory
);
void* data;
vkMapMemory(xeDevice.device(), stagingBufferMemory, 0, imageSize, 0, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(xeDevice.device(), stagingBufferMemory);
stbi_image_free(pixels);
createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkDestroyBuffer(xeDevice.device(), stagingBuffer, nullptr);
vkFreeMemory(xeDevice.device(), stagingBufferMemory, nullptr);
}
void XeImage::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateImage(xeDevice.device(), &imageInfo, nullptr, &image) != VK_SUCCESS) {
throw std::runtime_error("failed to create image!");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(xeDevice.device(), image, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = xeDevice.findMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(xeDevice.device(), &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate image memory!");
}
vkBindImageMemory(xeDevice.device(), image, imageMemory, 0);
}
void XeImage::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
VkCommandBuffer commandBuffer = xeDevice.beginSingleTimeCommands();
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
throw std::invalid_argument("unsupported layout transition!");
}
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
xeDevice.endSingleTimeCommands(commandBuffer);
}
void XeImage::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
VkCommandBuffer commandBuffer = xeDevice.beginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {
width,
height,
1
};
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
xeDevice.endSingleTimeCommands(commandBuffer);
}
}

35
engine/xe_image.hpp Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "xe_device.hpp"
#include <string>
namespace xe {
class XeImage {
public:
XeImage(XeDevice &xeDevice, const std::string &filename);
~XeImage();
XeImage(const XeImage&) = delete;
XeImage operator=(const XeImage&) = delete;
private:
void createTextureImage(const std::string &filename);
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
XeDevice &xeDevice;
VkImage textureImage;
VkDeviceMemory textureImageMemory;
friend class XeRenderSystem;
};
}

View file

@ -21,19 +21,69 @@ XeRenderSystem::XeRenderSystem(
std::string vert, std::string vert,
std::string frag, std::string frag,
uint32_t pushCunstantDataSize, uint32_t pushCunstantDataSize,
uint32_t uniformBufferDataSize) uint32_t uniformBufferDataSize,
: xeDevice{xeEngine.xeDevice}, xeRenderer{xeEngine.xeRenderer} { XeImage *image
createUniformBuffers(*xeEngine.xeDescriptorPool, *xeEngine.xeDescriptorSetLayout, uniformBufferDataSize); ) : xeDevice{xeEngine.xeDevice},
createPipelineLayout(*xeEngine.xeDescriptorSetLayout, pushCunstantDataSize, uniformBufferDataSize); xeRenderer{xeEngine.xeRenderer},
pushCunstantDataSize{pushCunstantDataSize},
uniformBufferDataSize{uniformBufferDataSize},
textureSamplerBinding{image != nullptr} {
createDescriptorSetLayout();
createUniformBuffers();
createTextureImageView(image);
createDescriptorSets(*xeEngine.xeDescriptorPool);
createPipelineLayout();
createPipeline(xeRenderer.getSwapChainRenderPass(), vert, frag); createPipeline(xeRenderer.getSwapChainRenderPass(), vert, frag);
} }
XeRenderSystem::~XeRenderSystem() { XeRenderSystem::~XeRenderSystem() {
vkDestroyPipelineLayout(xeDevice.device(), pipelineLayout, nullptr); vkDestroyPipelineLayout(xeDevice.device(), pipelineLayout, nullptr);
if ( textureSamplerBinding ) {
vkDestroySampler(xeDevice.device(), textureSampler, nullptr);
vkDestroyImageView(xeDevice.device(), textureImageView, nullptr);
}
}; };
void XeRenderSystem::createUniformBuffers(XeDescriptorPool &xeDescriptorPool, XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t uniformBufferDataSize) { void XeRenderSystem::createDescriptorSetLayout() {
XeDescriptorSetLayout::Builder builder{xeDevice};
int binding = 0;
if (uniformBufferDataSize > 0) {
builder.addBinding(binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, nullptr);
binding += 1;
}
if (textureSamplerBinding) {
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(xeDevice.device(), &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
}
builder.addBinding(binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, &textureSampler);
binding += 1;
}
xeDescriptorSetLayout = builder.build();
}
void XeRenderSystem::createUniformBuffers() {
if(uniformBufferDataSize == 0) return; if(uniformBufferDataSize == 0) return;
uboBuffers = std::vector<std::unique_ptr<XeBuffer>>(XeSwapChain::MAX_FRAMES_IN_FLIGHT); uboBuffers = std::vector<std::unique_ptr<XeBuffer>>(XeSwapChain::MAX_FRAMES_IN_FLIGHT);
@ -42,22 +92,65 @@ void XeRenderSystem::createUniformBuffers(XeDescriptorPool &xeDescriptorPool, Xe
xeDevice, xeDevice,
uniformBufferDataSize, uniformBufferDataSize,
XeSwapChain::MAX_FRAMES_IN_FLIGHT, XeSwapChain::MAX_FRAMES_IN_FLIGHT,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
uboBuffers[i]->map(); uboBuffers[i]->map();
} }
}
void XeRenderSystem::createTextureImageView(XeImage *image) {
if (!textureSamplerBinding) {
return;
}
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image->textureImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(xeDevice.device(), &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture image view!");
}
}
void XeRenderSystem::createDescriptorSets(XeDescriptorPool &xeDescriptorPool) {
descriptorSets = std::vector<VkDescriptorSet>(XeSwapChain::MAX_FRAMES_IN_FLIGHT); descriptorSets = std::vector<VkDescriptorSet>(XeSwapChain::MAX_FRAMES_IN_FLIGHT);
for (int i = 0; i < descriptorSets.size(); i++) { for (int i = 0; i < descriptorSets.size(); i++) {
auto bufferInfo = uboBuffers[i]->descriptorInfo(); auto bufferInfo = uboBuffers[i]->descriptorInfo();
XeDescriptorWriter(xeDescriptorSetLayout, xeDescriptorPool) XeDescriptorWriter writer{*xeDescriptorSetLayout, xeDescriptorPool};
.writeBuffer(0, &bufferInfo)
.build(descriptorSets[i]); int binding = 0;
if (uniformBufferDataSize > 0) {
writer.writeBuffer(binding, &bufferInfo);
binding += 1;
} }
if (textureSamplerBinding) {
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = textureImageView;
imageInfo.sampler = textureSampler;
writer.writeImage(binding, &imageInfo);
binding += 1;
}
writer.build(descriptorSets[i]);
}
} }
void XeRenderSystem::createPipelineLayout(XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t pushCunstantDataSize, uint32_t uniformBufferDataSize) { void XeRenderSystem::createPipelineLayout() {
VkPushConstantRange pushConstantRange; VkPushConstantRange pushConstantRange;
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
@ -75,12 +168,13 @@ void XeRenderSystem::createPipelineLayout(XeDescriptorSetLayout &xeDescriptorSet
pipelineLayoutInfo.pPushConstantRanges = nullptr; pipelineLayoutInfo.pPushConstantRanges = nullptr;
} }
std::vector<VkDescriptorSetLayout> descriptorSetLayouts{xeDescriptorSetLayout.getDescriptorSetLayout()}; std::vector<VkDescriptorSetLayout> descriptorSetLayouts{xeDescriptorSetLayout->getDescriptorSetLayout()};
if (uniformBufferDataSize > 0) { if (uniformBufferDataSize > 0) {
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size()); pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
} else { } else {
pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pSetLayouts = nullptr; pipelineLayoutInfo.pSetLayouts = nullptr;
} }
@ -107,13 +201,11 @@ void XeRenderSystem::createPipeline(VkRenderPass renderPass, std::string vert, s
); );
} }
void XeRenderSystem::loadPushConstant(void *pushConstantData, uint32_t pushConstantSize) { void XeRenderSystem::start() {
if(!boundPipeline) {
xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer());
xePipeline->bind(xeRenderer.getCurrentCommandBuffer()); xePipeline->bind(xeRenderer.getCurrentCommandBuffer());
boundPipeline = true; if(descriptorSets.size() > 0) {
}
if(!boundDescriptor) {
vkCmdBindDescriptorSets( vkCmdBindDescriptorSets(
xeRenderer.getCurrentCommandBuffer(), xeRenderer.getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_GRAPHICS,
@ -123,28 +215,29 @@ void XeRenderSystem::loadPushConstant(void *pushConstantData, uint32_t pushConst
&descriptorSets[xeRenderer.getFrameIndex()], &descriptorSets[xeRenderer.getFrameIndex()],
0, 0,
nullptr); nullptr);
boundDescriptor = true;
} }
}
void XeRenderSystem::loadPushConstant(void *pushConstantData) {
vkCmdPushConstants( vkCmdPushConstants(
xeRenderer.getCurrentCommandBuffer(), xeRenderer.getCurrentCommandBuffer(),
pipelineLayout, pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, 0,
pushConstantSize, pushCunstantDataSize,
pushConstantData); pushConstantData);
} }
void XeRenderSystem::loadUniformObject(void *uniformBufferData, uint32_t uniformBufferSize) { void XeRenderSystem::loadUniformObject(void *uniformBufferData) {
uboBuffers[xeRenderer.getFrameIndex()]->writeToBuffer(uniformBufferData); uboBuffers[xeRenderer.getFrameIndex()]->writeToBuffer(uniformBufferData);
uboBuffers[xeRenderer.getFrameIndex()]->flush(); }
void XeRenderSystem::loadTexture(XeImage *image) {
// createTextureImageView(image);
} }
void XeRenderSystem::render(XeGameObject &gameObject) { void XeRenderSystem::render(XeGameObject &gameObject) {
if(!boundPipeline){
xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer());
xePipeline->bind(xeRenderer.getCurrentCommandBuffer());
boundPipeline = true;
}
gameObject.model->bind(xeRenderer.getCurrentCommandBuffer()); gameObject.model->bind(xeRenderer.getCurrentCommandBuffer());
gameObject.model->draw(xeRenderer.getCurrentCommandBuffer()); gameObject.model->draw(xeRenderer.getCurrentCommandBuffer());
@ -152,8 +245,6 @@ void XeRenderSystem::render(XeGameObject &gameObject) {
} }
void XeRenderSystem::stop() { void XeRenderSystem::stop() {
boundPipeline = false;
boundDescriptor = false;
xeRenderer.endSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); xeRenderer.endSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer());
} }

View file

@ -7,6 +7,7 @@
#include "xe_descriptors.hpp" #include "xe_descriptors.hpp"
#include "xe_renderer.hpp" #include "xe_renderer.hpp"
#include "xe_engine.hpp" #include "xe_engine.hpp"
#include "xe_image.hpp"
#include <memory> #include <memory>
@ -20,7 +21,8 @@ class XeRenderSystem {
std::string vert, std::string vert,
std::string frag, std::string frag,
uint32_t pushCunstantDataSize, uint32_t pushCunstantDataSize,
uint32_t uniformBufferDataSize uint32_t uniformBufferDataSize,
XeImage *image
); );
~XeRenderSystem(); ~XeRenderSystem();
@ -28,20 +30,29 @@ class XeRenderSystem {
XeRenderSystem(const XeRenderSystem &) = delete; XeRenderSystem(const XeRenderSystem &) = delete;
XeRenderSystem operator=(const XeRenderSystem &) = delete; XeRenderSystem operator=(const XeRenderSystem &) = delete;
void loadPushConstant(void *pushConstantData, uint32_t pushConstantSize); void start();
void loadUniformObject(void *uniformBufferData, uint32_t uniformBufferSize); void loadPushConstant(void *pushConstantData);
void loadUniformObject(void *uniformBufferData);
void loadTexture(XeImage *image);
void render(XeGameObject &gameObject); void render(XeGameObject &gameObject);
void stop(); void stop();
private: private:
void createUniformBuffers(XeDescriptorPool &xeDescriptorPool, XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t uniformBufferDataSize); void createDescriptorSetLayout();
void createPipelineLayout(XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t pushCunstantDataSize, uint32_t uniformBufferDataSize); void createUniformBuffers();
void createTextureImageView(XeImage *image);
void createDescriptorSets(XeDescriptorPool &xeDescriptorPool);
void createPipelineLayout();
void createPipeline(VkRenderPass renderPass, std::string vert, std::string frag); void createPipeline(VkRenderPass renderPass, std::string vert, std::string frag);
bool boundPipeline{false}; bool boundPipeline{false};
bool boundDescriptor{false}; bool boundDescriptor{false};
uint32_t uniformBufferDataSize;
uint32_t pushCunstantDataSize;
bool textureSamplerBinding;
XeDevice& xeDevice; XeDevice& xeDevice;
XeRenderer& xeRenderer; XeRenderer& xeRenderer;
@ -49,7 +60,11 @@ class XeRenderSystem {
std::vector<std::unique_ptr<XeBuffer>> uboBuffers; std::vector<std::unique_ptr<XeBuffer>> uboBuffers;
std::vector<VkDescriptorSet> descriptorSets; std::vector<VkDescriptorSet> descriptorSets;
VkSampler textureSampler;
VkImageView textureImageView;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
std::unique_ptr<XeDescriptorSetLayout> xeDescriptorSetLayout;
}; };

View file

@ -13,7 +13,7 @@ namespace xe {
class XeSwapChain { class XeSwapChain {
public: public:
static constexpr int MAX_FRAMES_IN_FLIGHT = 2; static constexpr int MAX_FRAMES_IN_FLIGHT = 3;
XeSwapChain(XeDevice &deviceRef, VkExtent2D windowExtent); XeSwapChain(XeDevice &deviceRef, VkExtent2D windowExtent);
XeSwapChain(XeDevice &deviceRef, VkExtent2D windowExtent, std::shared_ptr<XeSwapChain> previous); XeSwapChain(XeDevice &deviceRef, VkExtent2D windowExtent, std::shared_ptr<XeSwapChain> previous);

1
lib/stb Submodule

@ -0,0 +1 @@
Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55

BIN
res/image/scaly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
res/image/texture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -1,19 +1,22 @@
#version 450 #version 450
layout (location = 0) in vec3 fragColor; layout (location = 0) in vec3 fragColor;
layout (location = 1) in vec2 fragUv;
layout (location = 0) out vec4 outColor; layout (location = 0) out vec4 outColor;
layout(set = 0, binding = 0) uniform GlobalUbo { layout (binding = 0) uniform GlobalUbo {
mat4 projectionViewMatrix; mat4 projectionViewMatrix;
vec3 directionToLight; vec3 directionToLight;
} ubo; } ubo;
layout (binding = 1) uniform sampler2D texSampler;
layout(push_constant) uniform Push { layout(push_constant) uniform Push {
mat4 transform; mat4 transform;
mat4 normalMatrix; mat4 normalMatrix;
} push; } push;
void main() { void main() {
outColor = vec4(fragColor, 1.0); outColor = mix(texture(texSampler, fragUv), vec4(fragColor, 1.0), .5);
} }

Binary file not shown.

View file

@ -1,18 +1,19 @@
#version 450 #version 450
layout(location = 0) in vec3 position; layout (location = 0) in vec3 position;
layout(location = 1) in vec3 color; layout (location = 1) in vec3 color;
layout(location = 2) in vec3 normal; layout (location = 2) in vec3 normal;
layout(location = 3) in vec2 uv; layout (location = 3) in vec2 uv;
layout(location = 0) out vec3 fragColor; layout (location = 0) out vec3 fragColor;
layout (location = 1) out vec2 fragUv;
layout(set = 0, binding = 0) uniform GlobalUbo { layout (binding = 0) uniform GlobalUbo {
mat4 projectionViewMatrix; mat4 projectionViewMatrix;
vec3 directionToLight; vec3 directionToLight;
} ubo; } ubo;
layout(push_constant) uniform Push { layout (push_constant) uniform Push {
mat4 modelMatrix; mat4 modelMatrix;
mat4 normalMatrix; mat4 normalMatrix;
} push; } push;
@ -27,4 +28,5 @@ void main() {
float lightIntensity = AMBIENT + max(dot(normalWorldSpace, ubo.directionToLight), 0); float lightIntensity = AMBIENT + max(dot(normalWorldSpace, ubo.directionToLight), 0);
fragColor = lightIntensity * vec3(1/position.y,position.y,clamp(sin(position.x - position.z), 0, 1)); fragColor = lightIntensity * vec3(1/position.y,position.y,clamp(sin(position.x - position.z), 0, 1));
fragUv = uv;
} }

Binary file not shown.

View file

@ -14,6 +14,8 @@
#include <glm/gtc/constants.hpp> #include <glm/gtc/constants.hpp>
#include <array> #include <array>
#include <string>
namespace app { namespace app {
FirstApp::FirstApp() : xeEngine{WIDTH, HEIGHT, "Hello, Vulkan!"} { FirstApp::FirstApp() : xeEngine{WIDTH, HEIGHT, "Hello, Vulkan!"} {
@ -24,7 +26,12 @@ FirstApp::~FirstApp() {}
void FirstApp::run() { void FirstApp::run() {
SimpleRenderer renderer{xeEngine}; const std::string s = "res/image/texture.png";
std::shared_ptr<xe::XeImage> image = xeEngine.loadImage("res/image/texture.png");
std::shared_ptr<xe::XeImage> image2 = xeEngine.loadImage("res/image/scaly.png");
SimpleRenderer renderer{xeEngine, image.get()};
auto viewerObject = xe::XeGameObject::createGameObject(); auto viewerObject = xe::XeGameObject::createGameObject();
viewerObject.transform.translation = {-7.f, 3.f, -7.f}; viewerObject.transform.translation = {-7.f, 3.f, -7.f};
@ -39,7 +46,7 @@ void FirstApp::run() {
xeEngine.getCamera().setViewYXZ(viewerObject.transform.translation, viewerObject.transform.rotation); xeEngine.getCamera().setViewYXZ(viewerObject.transform.translation, viewerObject.transform.rotation);
if(xeEngine.beginFrame()) { if(xeEngine.beginFrame()) {
renderer.render(gameObjects, xeEngine.getCamera()); renderer.render(gameObjects, xeEngine.getCamera(), image2.get());
xeEngine.endFrame(); xeEngine.endFrame();
} }
} }

View file

@ -2,20 +2,23 @@
namespace app { namespace app {
SimpleRenderer::SimpleRenderer(xe::XeEngine &xeEngine) SimpleRenderer::SimpleRenderer(xe::XeEngine &xeEngine, xe::XeImage *xeImage)
: xeRenderSystem{xeEngine, "res/shaders/simple_shader.vert.spv", "res/shaders/simple_shader.frag.spv", sizeof(PushConstant), sizeof(UniformBuffer)} {}; : xeRenderSystem{xeEngine, "res/shaders/simple_shader.vert.spv", "res/shaders/simple_shader.frag.spv", sizeof(PushConstant), sizeof(UniformBuffer), xeImage} {};
void SimpleRenderer::render(std::vector<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera) { void SimpleRenderer::render(std::vector<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera, xe::XeImage *xeImage) {
xeRenderSystem.start();
UniformBuffer ubo{}; UniformBuffer ubo{};
ubo.projectionView = xeCamera.getProjection() * xeCamera.getView(); ubo.projectionView = xeCamera.getProjection() * xeCamera.getView();
xeRenderSystem.loadUniformObject(&ubo, sizeof(ubo)); xeRenderSystem.loadUniformObject(&ubo);
xeRenderSystem.loadTexture(xeImage);
for(auto &obj : gameObjects) { for(auto &obj : gameObjects) {
PushConstant pc{}; PushConstant pc{};
pc.modelMatrix = obj.transform.mat4(); pc.modelMatrix = obj.transform.mat4();
pc.normalMatrix = obj.transform.normalMatrix(); pc.normalMatrix = obj.transform.normalMatrix();
xeRenderSystem.loadPushConstant(&pc, sizeof(pc)); xeRenderSystem.loadPushConstant(&pc);
xeRenderSystem.render(obj); xeRenderSystem.render(obj);
} }

View file

@ -19,14 +19,14 @@ class SimpleRenderer {
public: public:
SimpleRenderer(xe::XeEngine &xeEngine); SimpleRenderer(xe::XeEngine &xeEngine, xe::XeImage *xeImage);
~SimpleRenderer() {}; ~SimpleRenderer() {};
SimpleRenderer(const SimpleRenderer&) = delete; SimpleRenderer(const SimpleRenderer&) = delete;
SimpleRenderer operator=(const SimpleRenderer&) = delete; SimpleRenderer operator=(const SimpleRenderer&) = delete;
void render(std::vector<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera); void render(std::vector<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera, xe::XeImage *xeImage);
private: private:
xe::XeRenderSystem xeRenderSystem; xe::XeRenderSystem xeRenderSystem;