diff --git a/.gitmodules b/.gitmodules index 4214750..c48bf84 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "glm"] path = lib/glm url = https://github.com/g-truc/glm.git +[submodule "stb"] + path = lib/stb + url = https://github.com/nothings/stb.git diff --git a/Makefile b/Makefile index 561c5bf..a97fd1d 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ INCFLAGS = -Isrc INCFLAGS += -Iengine INCFLAGS += -Ilib/glfw/include INCFLAGS += -Ilib/glm +INCFLAGS += -Ilib/stb CCFLAGS = -std=c++17 -O2 -g CCFLAGS += $(INCFLAGS) diff --git a/engine/xe_descriptors.cpp b/engine/xe_descriptors.cpp index ef6dc2c..f1f4436 100644 --- a/engine/xe_descriptors.cpp +++ b/engine/xe_descriptors.cpp @@ -9,13 +9,15 @@ XeDescriptorSetLayout::Builder &XeDescriptorSetLayout::Builder::addBinding( uint32_t binding, VkDescriptorType descriptorType, VkShaderStageFlags stageFlags, - uint32_t count) { + VkSampler *sampler) { assert(bindings.count(binding) == 0 && "Binding already in use"); + uint32_t count = 1; VkDescriptorSetLayoutBinding layoutBinding{}; layoutBinding.binding = binding; layoutBinding.descriptorType = descriptorType; layoutBinding.descriptorCount = count; layoutBinding.stageFlags = stageFlags; + layoutBinding.pImmutableSamplers = sampler; bindings[binding] = layoutBinding; return *this; } diff --git a/engine/xe_descriptors.hpp b/engine/xe_descriptors.hpp index 6e7950e..5efe9b4 100644 --- a/engine/xe_descriptors.hpp +++ b/engine/xe_descriptors.hpp @@ -19,7 +19,7 @@ class XeDescriptorSetLayout { uint32_t binding, VkDescriptorType descriptorType, VkShaderStageFlags stageFlags, - uint32_t count = 1); + VkSampler *sampler); std::unique_ptr build() const; private: diff --git a/engine/xe_engine.cpp b/engine/xe_engine.cpp index ea54bb4..6a971c9 100644 --- a/engine/xe_engine.cpp +++ b/engine/xe_engine.cpp @@ -1,4 +1,5 @@ #include "xe_engine.hpp" +#include "xe_image.hpp" #include namespace xe { @@ -7,18 +8,15 @@ XeEngine::XeEngine(int width, int height, std::string name) : xeWindow{width, he xeDevice{xeWindow}, xeRenderer{xeWindow, xeDevice}, xeCamera{} { - loadDescriptors(); + loadDescriptorPool(); }; -void XeEngine::loadDescriptors() { +void XeEngine::loadDescriptorPool() { xeDescriptorPool = XeDescriptorPool::Builder(xeDevice) .setMaxSets(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) - .build(); - - xeDescriptorSetLayout = XeDescriptorSetLayout::Builder(xeDevice) - .addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT) + .addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, XeSwapChain::MAX_FRAMES_IN_FLIGHT) .build(); } @@ -35,6 +33,10 @@ std::shared_ptr XeEngine::loadModelFromData(std::vector(xeDevice, builder); } +std::shared_ptr XeEngine::loadImage(const std::string &filename) { + return std::make_shared(xeDevice, filename); +} + bool XeEngine::poll() { glfwPollEvents(); auto newTime = std::chrono::high_resolution_clock::now(); diff --git a/engine/xe_engine.hpp b/engine/xe_engine.hpp index d3862d4..5731b50 100644 --- a/engine/xe_engine.hpp +++ b/engine/xe_engine.hpp @@ -5,8 +5,9 @@ #include "xe_renderer.hpp" #include "xe_camera.hpp" #include "xe_descriptors.hpp" -#include +#include "xe_image.hpp" +#include #include namespace xe { @@ -26,7 +27,8 @@ class XeEngine { std::shared_ptr loadModelFromFile(const std::string &filename); std::shared_ptr loadModelFromData(std::vector vertices, std::vector indices); - + std::shared_ptr loadImage(const std::string &filename); + bool beginFrame() { return xeRenderer.beginFrame(); } void endFrame() { xeRenderer.endFrame(); } void close() { vkDeviceWaitIdle(xeDevice.device()); } @@ -36,7 +38,7 @@ class XeEngine { private: - void loadDescriptors(); + void loadDescriptorPool(); XeWindow xeWindow; XeDevice xeDevice; @@ -49,7 +51,6 @@ class XeEngine { float FOV = 50.f; std::unique_ptr xeDescriptorPool; - std::unique_ptr xeDescriptorSetLayout; friend class XeRenderSystem; }; diff --git a/engine/xe_image.cpp b/engine/xe_image.cpp new file mode 100644 index 0000000..c7b1488 --- /dev/null +++ b/engine/xe_image.cpp @@ -0,0 +1,166 @@ +#include "xe_image.hpp" + +#include +#include +#include +#include + +#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(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(texWidth), static_cast(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, ®ion); + + xeDevice.endSingleTimeCommands(commandBuffer); + } + +} \ No newline at end of file diff --git a/engine/xe_image.hpp b/engine/xe_image.hpp new file mode 100644 index 0000000..c3ffe4f --- /dev/null +++ b/engine/xe_image.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "xe_device.hpp" + +#include + +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; + +}; + +} diff --git a/engine/xe_render_system.cpp b/engine/xe_render_system.cpp index e713884..80f76af 100644 --- a/engine/xe_render_system.cpp +++ b/engine/xe_render_system.cpp @@ -21,19 +21,69 @@ XeRenderSystem::XeRenderSystem( std::string vert, std::string frag, uint32_t pushCunstantDataSize, - uint32_t uniformBufferDataSize) - : xeDevice{xeEngine.xeDevice}, xeRenderer{xeEngine.xeRenderer} { - createUniformBuffers(*xeEngine.xeDescriptorPool, *xeEngine.xeDescriptorSetLayout, uniformBufferDataSize); - createPipelineLayout(*xeEngine.xeDescriptorSetLayout, pushCunstantDataSize, uniformBufferDataSize); + uint32_t uniformBufferDataSize, + XeImage *image +) : xeDevice{xeEngine.xeDevice}, + xeRenderer{xeEngine.xeRenderer}, + pushCunstantDataSize{pushCunstantDataSize}, + uniformBufferDataSize{uniformBufferDataSize}, + textureSamplerBinding{image != nullptr} { + createDescriptorSetLayout(); + createUniformBuffers(); + createTextureImageView(image); + createDescriptorSets(*xeEngine.xeDescriptorPool); + createPipelineLayout(); createPipeline(xeRenderer.getSwapChainRenderPass(), vert, frag); } XeRenderSystem::~XeRenderSystem() { 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; uboBuffers = std::vector>(XeSwapChain::MAX_FRAMES_IN_FLIGHT); @@ -42,22 +92,65 @@ void XeRenderSystem::createUniformBuffers(XeDescriptorPool &xeDescriptorPool, Xe xeDevice, uniformBufferDataSize, XeSwapChain::MAX_FRAMES_IN_FLIGHT, + 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(); } +} + +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(XeSwapChain::MAX_FRAMES_IN_FLIGHT); for (int i = 0; i < descriptorSets.size(); i++) { auto bufferInfo = uboBuffers[i]->descriptorInfo(); - XeDescriptorWriter(xeDescriptorSetLayout, xeDescriptorPool) - .writeBuffer(0, &bufferInfo) - .build(descriptorSets[i]); + XeDescriptorWriter writer{*xeDescriptorSetLayout, xeDescriptorPool}; + + 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; pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; @@ -75,12 +168,13 @@ void XeRenderSystem::createPipelineLayout(XeDescriptorSetLayout &xeDescriptorSet pipelineLayoutInfo.pPushConstantRanges = nullptr; } - std::vector descriptorSetLayouts{xeDescriptorSetLayout.getDescriptorSetLayout()}; + std::vector descriptorSetLayouts{xeDescriptorSetLayout->getDescriptorSetLayout()}; if (uniformBufferDataSize > 0) { pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); } else { + pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pSetLayouts = nullptr; } @@ -107,13 +201,11 @@ void XeRenderSystem::createPipeline(VkRenderPass renderPass, std::string vert, s ); } -void XeRenderSystem::loadPushConstant(void *pushConstantData, uint32_t pushConstantSize) { - if(!boundPipeline) { - xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); - xePipeline->bind(xeRenderer.getCurrentCommandBuffer()); - boundPipeline = true; - } - if(!boundDescriptor) { +void XeRenderSystem::start() { + xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); + xePipeline->bind(xeRenderer.getCurrentCommandBuffer()); + if(descriptorSets.size() > 0) { + vkCmdBindDescriptorSets( xeRenderer.getCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -123,28 +215,29 @@ void XeRenderSystem::loadPushConstant(void *pushConstantData, uint32_t pushConst &descriptorSets[xeRenderer.getFrameIndex()], 0, nullptr); - boundDescriptor = true; + } +} + +void XeRenderSystem::loadPushConstant(void *pushConstantData) { vkCmdPushConstants( xeRenderer.getCurrentCommandBuffer(), pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, - pushConstantSize, + pushCunstantDataSize, pushConstantData); } -void XeRenderSystem::loadUniformObject(void *uniformBufferData, uint32_t uniformBufferSize) { +void XeRenderSystem::loadUniformObject(void *uniformBufferData) { uboBuffers[xeRenderer.getFrameIndex()]->writeToBuffer(uniformBufferData); - uboBuffers[xeRenderer.getFrameIndex()]->flush(); +} + +void XeRenderSystem::loadTexture(XeImage *image) { + // createTextureImageView(image); } void XeRenderSystem::render(XeGameObject &gameObject) { - if(!boundPipeline){ - xeRenderer.beginSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); - xePipeline->bind(xeRenderer.getCurrentCommandBuffer()); - boundPipeline = true; - } gameObject.model->bind(xeRenderer.getCurrentCommandBuffer()); gameObject.model->draw(xeRenderer.getCurrentCommandBuffer()); @@ -152,8 +245,6 @@ void XeRenderSystem::render(XeGameObject &gameObject) { } void XeRenderSystem::stop() { - boundPipeline = false; - boundDescriptor = false; xeRenderer.endSwapChainRenderPass(xeRenderer.getCurrentCommandBuffer()); } diff --git a/engine/xe_render_system.hpp b/engine/xe_render_system.hpp index fa56fdd..fbf10fc 100644 --- a/engine/xe_render_system.hpp +++ b/engine/xe_render_system.hpp @@ -7,6 +7,7 @@ #include "xe_descriptors.hpp" #include "xe_renderer.hpp" #include "xe_engine.hpp" +#include "xe_image.hpp" #include @@ -20,7 +21,8 @@ class XeRenderSystem { std::string vert, std::string frag, uint32_t pushCunstantDataSize, - uint32_t uniformBufferDataSize + uint32_t uniformBufferDataSize, + XeImage *image ); ~XeRenderSystem(); @@ -28,20 +30,29 @@ class XeRenderSystem { XeRenderSystem(const XeRenderSystem &) = delete; XeRenderSystem operator=(const XeRenderSystem &) = delete; - void loadPushConstant(void *pushConstantData, uint32_t pushConstantSize); - void loadUniformObject(void *uniformBufferData, uint32_t uniformBufferSize); + void start(); + void loadPushConstant(void *pushConstantData); + void loadUniformObject(void *uniformBufferData); + void loadTexture(XeImage *image); void render(XeGameObject &gameObject); void stop(); private: - void createUniformBuffers(XeDescriptorPool &xeDescriptorPool, XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t uniformBufferDataSize); - void createPipelineLayout(XeDescriptorSetLayout &xeDescriptorSetLayout, uint32_t pushCunstantDataSize, uint32_t uniformBufferDataSize); + void createDescriptorSetLayout(); + void createUniformBuffers(); + void createTextureImageView(XeImage *image); + void createDescriptorSets(XeDescriptorPool &xeDescriptorPool); + void createPipelineLayout(); void createPipeline(VkRenderPass renderPass, std::string vert, std::string frag); bool boundPipeline{false}; bool boundDescriptor{false}; + uint32_t uniformBufferDataSize; + uint32_t pushCunstantDataSize; + bool textureSamplerBinding; + XeDevice& xeDevice; XeRenderer& xeRenderer; @@ -49,7 +60,11 @@ class XeRenderSystem { std::vector> uboBuffers; std::vector descriptorSets; + VkSampler textureSampler; + VkImageView textureImageView; + VkPipelineLayout pipelineLayout; + std::unique_ptr xeDescriptorSetLayout; }; diff --git a/engine/xe_swap_chain.hpp b/engine/xe_swap_chain.hpp index 50aa03f..c0299a3 100755 --- a/engine/xe_swap_chain.hpp +++ b/engine/xe_swap_chain.hpp @@ -13,7 +13,7 @@ namespace xe { class XeSwapChain { 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, std::shared_ptr previous); diff --git a/lib/stb b/lib/stb new file mode 160000 index 0000000..8b5f1f3 --- /dev/null +++ b/lib/stb @@ -0,0 +1 @@ +Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55 diff --git a/res/image/scaly.png b/res/image/scaly.png new file mode 100644 index 0000000..cefdd5f Binary files /dev/null and b/res/image/scaly.png differ diff --git a/res/image/texture.png b/res/image/texture.png new file mode 100644 index 0000000..e1db6c1 Binary files /dev/null and b/res/image/texture.png differ diff --git a/res/shaders/simple_shader.frag b/res/shaders/simple_shader.frag index b11a5a8..4eaaafc 100755 --- a/res/shaders/simple_shader.frag +++ b/res/shaders/simple_shader.frag @@ -1,19 +1,22 @@ #version 450 layout (location = 0) in vec3 fragColor; +layout (location = 1) in vec2 fragUv; layout (location = 0) out vec4 outColor; -layout(set = 0, binding = 0) uniform GlobalUbo { +layout (binding = 0) uniform GlobalUbo { mat4 projectionViewMatrix; vec3 directionToLight; } ubo; +layout (binding = 1) uniform sampler2D texSampler; + layout(push_constant) uniform Push { mat4 transform; mat4 normalMatrix; } push; void main() { - outColor = vec4(fragColor, 1.0); + outColor = mix(texture(texSampler, fragUv), vec4(fragColor, 1.0), .5); } \ No newline at end of file diff --git a/res/shaders/simple_shader.frag.spv b/res/shaders/simple_shader.frag.spv index ff80cc6..a6afbfe 100644 Binary files a/res/shaders/simple_shader.frag.spv and b/res/shaders/simple_shader.frag.spv differ diff --git a/res/shaders/simple_shader.vert b/res/shaders/simple_shader.vert index 55b0199..a61fa0b 100755 --- a/res/shaders/simple_shader.vert +++ b/res/shaders/simple_shader.vert @@ -1,18 +1,19 @@ #version 450 -layout(location = 0) in vec3 position; -layout(location = 1) in vec3 color; -layout(location = 2) in vec3 normal; -layout(location = 3) in vec2 uv; +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 color; +layout (location = 2) in vec3 normal; +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; vec3 directionToLight; } ubo; -layout(push_constant) uniform Push { +layout (push_constant) uniform Push { mat4 modelMatrix; mat4 normalMatrix; } push; @@ -27,4 +28,5 @@ void main() { 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)); + fragUv = uv; } \ No newline at end of file diff --git a/res/shaders/simple_shader.vert.spv b/res/shaders/simple_shader.vert.spv index bba7f70..84c0f48 100644 Binary files a/res/shaders/simple_shader.vert.spv and b/res/shaders/simple_shader.vert.spv differ diff --git a/src/first_app.cpp b/src/first_app.cpp index cff92db..40a5681 100755 --- a/src/first_app.cpp +++ b/src/first_app.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace app { FirstApp::FirstApp() : xeEngine{WIDTH, HEIGHT, "Hello, Vulkan!"} { @@ -24,7 +26,12 @@ FirstApp::~FirstApp() {} void FirstApp::run() { - SimpleRenderer renderer{xeEngine}; + const std::string s = "res/image/texture.png"; + + std::shared_ptr image = xeEngine.loadImage("res/image/texture.png"); + std::shared_ptr image2 = xeEngine.loadImage("res/image/scaly.png"); + + SimpleRenderer renderer{xeEngine, image.get()}; auto viewerObject = xe::XeGameObject::createGameObject(); 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); if(xeEngine.beginFrame()) { - renderer.render(gameObjects, xeEngine.getCamera()); + renderer.render(gameObjects, xeEngine.getCamera(), image2.get()); xeEngine.endFrame(); } } diff --git a/src/simple_renderer.cpp b/src/simple_renderer.cpp index b4c115a..548b77d 100644 --- a/src/simple_renderer.cpp +++ b/src/simple_renderer.cpp @@ -2,20 +2,23 @@ namespace app { -SimpleRenderer::SimpleRenderer(xe::XeEngine &xeEngine) - : xeRenderSystem{xeEngine, "res/shaders/simple_shader.vert.spv", "res/shaders/simple_shader.frag.spv", sizeof(PushConstant), sizeof(UniformBuffer)} {}; +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), xeImage} {}; -void SimpleRenderer::render(std::vector &gameObjects, xe::XeCamera &xeCamera) { +void SimpleRenderer::render(std::vector &gameObjects, xe::XeCamera &xeCamera, xe::XeImage *xeImage) { + + xeRenderSystem.start(); UniformBuffer ubo{}; ubo.projectionView = xeCamera.getProjection() * xeCamera.getView(); - xeRenderSystem.loadUniformObject(&ubo, sizeof(ubo)); + xeRenderSystem.loadUniformObject(&ubo); + xeRenderSystem.loadTexture(xeImage); for(auto &obj : gameObjects) { PushConstant pc{}; pc.modelMatrix = obj.transform.mat4(); pc.normalMatrix = obj.transform.normalMatrix(); - xeRenderSystem.loadPushConstant(&pc, sizeof(pc)); + xeRenderSystem.loadPushConstant(&pc); xeRenderSystem.render(obj); } diff --git a/src/simple_renderer.hpp b/src/simple_renderer.hpp index 22ba0b4..20e3997 100644 --- a/src/simple_renderer.hpp +++ b/src/simple_renderer.hpp @@ -19,14 +19,14 @@ class SimpleRenderer { public: - SimpleRenderer(xe::XeEngine &xeEngine); + SimpleRenderer(xe::XeEngine &xeEngine, xe::XeImage *xeImage); ~SimpleRenderer() {}; SimpleRenderer(const SimpleRenderer&) = delete; SimpleRenderer operator=(const SimpleRenderer&) = delete; - void render(std::vector &gameObjects, xe::XeCamera &xeCamera); + void render(std::vector &gameObjects, xe::XeCamera &xeCamera, xe::XeImage *xeImage); private: xe::XeRenderSystem xeRenderSystem;