texture loading
This commit is contained in:
parent
b2af1ca699
commit
249f6c9fa3
21 changed files with 397 additions and 65 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
1
Makefile
1
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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class XeDescriptorSetLayout {
|
|||
uint32_t binding,
|
||||
VkDescriptorType descriptorType,
|
||||
VkShaderStageFlags stageFlags,
|
||||
uint32_t count = 1);
|
||||
VkSampler *sampler);
|
||||
std::unique_ptr<XeDescriptorSetLayout> build() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "xe_engine.hpp"
|
||||
#include "xe_image.hpp"
|
||||
#include <chrono>
|
||||
|
||||
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<XeModel> XeEngine::loadModelFromData(std::vector<XeModel::Vertex
|
|||
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() {
|
||||
glfwPollEvents();
|
||||
auto newTime = std::chrono::high_resolution_clock::now();
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
#include "xe_renderer.hpp"
|
||||
#include "xe_camera.hpp"
|
||||
#include "xe_descriptors.hpp"
|
||||
#include <chrono>
|
||||
#include "xe_image.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
namespace xe {
|
||||
|
||||
|
@ -26,7 +27,8 @@ class XeEngine {
|
|||
|
||||
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<XeImage> 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> xeDescriptorPool;
|
||||
std::unique_ptr<XeDescriptorSetLayout> xeDescriptorSetLayout;
|
||||
|
||||
friend class XeRenderSystem;
|
||||
};
|
||||
|
|
166
engine/xe_image.cpp
Normal file
166
engine/xe_image.cpp
Normal 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, ®ion);
|
||||
|
||||
xeDevice.endSingleTimeCommands(commandBuffer);
|
||||
}
|
||||
|
||||
}
|
35
engine/xe_image.hpp
Normal file
35
engine/xe_image.hpp
Normal 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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<std::unique_ptr<XeBuffer>>(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<VkDescriptorSet>(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<VkDescriptorSetLayout> descriptorSetLayouts{xeDescriptorSetLayout.getDescriptorSetLayout()};
|
||||
std::vector<VkDescriptorSetLayout> descriptorSetLayouts{xeDescriptorSetLayout->getDescriptorSetLayout()};
|
||||
|
||||
if (uniformBufferDataSize > 0) {
|
||||
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(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());
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "xe_descriptors.hpp"
|
||||
#include "xe_renderer.hpp"
|
||||
#include "xe_engine.hpp"
|
||||
#include "xe_image.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -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<std::unique_ptr<XeBuffer>> uboBuffers;
|
||||
std::vector<VkDescriptorSet> descriptorSets;
|
||||
|
||||
VkSampler textureSampler;
|
||||
VkImageView textureImageView;
|
||||
|
||||
VkPipelineLayout pipelineLayout;
|
||||
std::unique_ptr<XeDescriptorSetLayout> xeDescriptorSetLayout;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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<XeSwapChain> previous);
|
||||
|
|
1
lib/stb
Submodule
1
lib/stb
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55
|
BIN
res/image/scaly.png
Normal file
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
BIN
res/image/texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
|
@ -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);
|
||||
}
|
Binary file not shown.
|
@ -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;
|
||||
}
|
Binary file not shown.
|
@ -14,6 +14,8 @@
|
|||
#include <glm/gtc/constants.hpp>
|
||||
#include <array>
|
||||
|
||||
#include <string>
|
||||
|
||||
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<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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera) {
|
||||
void SimpleRenderer::render(std::vector<xe::XeGameObject> &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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera);
|
||||
void render(std::vector<xe::XeGameObject> &gameObjects, xe::XeCamera &xeCamera, xe::XeImage *xeImage);
|
||||
|
||||
private:
|
||||
xe::XeRenderSystem xeRenderSystem;
|
||||
|
|
Loading…
Reference in a new issue