updated for design change

This commit is contained in:
matthias@arch 2022-11-14 23:00:13 +01:00
parent c97ab508ac
commit c84c12b9e4
11 changed files with 240 additions and 131 deletions

View File

@ -1,3 +1,3 @@
CompileFlags: # Tweak the parse settings CompileFlags: # Tweak the parse settings
Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -I../external, -I../renderer, -I../shader, -I../utility,] Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -Idrawables, -Ivulkan_draw_strategies, -I../external, -I../renderer, -I../shader, -I../utility, -I../drawables, -I../vulkan_draw_strategies ]
# https://clangd.llvm.org/config # https://clangd.llvm.org/config

View File

@ -2362,7 +2362,7 @@ UML_LIMIT_NUM_FIELDS = 10
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES. # This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO DOT_UML_DETAILS = YES
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold # to display on a single line. If the actual line length exceeds this threshold
@ -2569,4 +2569,4 @@ GENERATE_LEGEND = YES
# plantuml temporary files. # plantuml temporary files.
# The default value is: YES. # The default value is: YES.
DOT_CLEANUP = YES DOT_CLEANUP = NO

View File

@ -1,8 +1,11 @@
#include "shape.hpp" #include "ds_vk_rectangle.hpp"
#include "print_draw_strategy.hpp"
#include "vulkan_instance.hpp" #include "vulkan_instance.hpp"
#include "renderer2D.hpp" #include "renderer2D.hpp"
#include "renderer3D.hpp" #include "renderer3D.hpp"
#include "vulkan_settings.hpp" #include "vulkan_settings.hpp"
#include "font.hpp"
#include <chrono> #include <chrono>
#include <ratio> #include <ratio>
@ -10,14 +13,13 @@
namespace gz::vlk { namespace gz::vlk {
int mainLoop() { int mainLoop() {
LogCreateInfo logCI { Log log(LogCreateInfo {
.logfile = "main.log", .logfile = "main.log",
.storeLog = false, .storeLog = false,
.prefix = "Main", .prefix = "Main",
.prefixColor = Color::BG_RED, .prefixColor = Color::BG_RED,
.timeColor = settings::VULKAN_MESSAGE_TIME_COLOR, .timeColor = settings::VULKAN_MESSAGE_TIME_COLOR,
}; });
Log log(std::move(logCI));
gz::SettingsManagerCreateInfo<VULKAN_SETTINGS_MAN_TYPES> smCI { gz::SettingsManagerCreateInfo<VULKAN_SETTINGS_MAN_TYPES> smCI {
.filepath = settings::CONFIG_FILE, .filepath = settings::CONFIG_FILE,
@ -27,6 +29,7 @@ namespace gz::vlk {
.throwExceptionWhenNewValueNotAllowed = true, .throwExceptionWhenNewValueNotAllowed = true,
}; };
log("Creating instance");
VulkanInstance vulkanInstance(smCI); VulkanInstance vulkanInstance(smCI);
vulkanInstance.init(); vulkanInstance.init();
TextureManager textureManager(vulkanInstance); TextureManager textureManager(vulkanInstance);
@ -34,42 +37,53 @@ namespace gz::vlk {
log("Startup complete. Drawing shapes"); log("Startup complete. Drawing shapes");
Rectangle rect9(-200, -400, 200, 200, { 0.0f, 1.0f, 0.0f}, "entities/sheep.png"); BufferManager<Vertex2D, uint32_t> bufferManager(vulkanInstance);
Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "blocks/leaves.png"); auto createRect = [&r2D, &bufferManager](auto posX, auto posY, auto width, auto height, const auto& texture) {
Rectangle rect2( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "blocks/crate.png"); return Rectangle(posX, posY, width, height, std::make_unique<RectangleTexturedVkDS>(RectangleTexturedVkDS(r2D, bufferManager, texture)));
Rectangle rect3( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "blocks/dirt.png"); /* return Rectangle(posX, poxY, width, height, std::make_unique<PrintDrawStrategy<Rectangle>>()); */
Rectangle rect5( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "items/sword.png"); };
Rectangle rect6( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "items/crossbow.png"); std::vector<Rectangle> rects;
Rectangle rect4(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "blocks/ice.png"); rects.emplace_back(createRect( 0, 0, 48, 48, "blocks/leaves.png"));
Rectangle rect7( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "special/hotbar.png"); rects.emplace_back(createRect( -190,-191, 192, 193, "blocks/crate.png"));
Rectangle rect8(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "special/func_nocol.png"); rects.emplace_back(createRect( 32, 120, 400, 400, "blocks/dirt.png"));
Rectangle rect10(-400, -400, 800, 800, { 0.0f, 1.0f, 0.0f}, "atlas"); rects.emplace_back(createRect( 90, 91, 92, 93, "items/sword.png"));
r2D.drawShape(&rect1); rects.emplace_back(createRect( 190, 191, 192, 193, "items/crossbow.png"));
r2D.drawShape(&rect2); rects.emplace_back(createRect(-600, -600, 800, 400, "blocks/ice.png"));
r2D.drawShape(&rect3); rects.emplace_back(createRect( 32, 400, 400, 100, "special/hotbar.png"));
r2D.drawShape(&rect4); rects.emplace_back(createRect(-600, -600, 800, 400, "special/func_nocol.png"));
r2D.drawShape(&rect5); rects.emplace_back(createRect(-200, -400, 200, 200, "entities/sheep.png"));
r2D.drawShape(&rect6); rects.emplace_back(createRect(-600, 0, 600, 600, "atlas_0"));
r2D.drawShape(&rect7); log("Initializing complete.");
r2D.drawShape(&rect8);
r2D.drawShape(&rect9);
r2D.drawShape(&rect10);
log("Drawing complete. Filling r2D with shapes.");
r2D.fillVertexBufferWithShapes();
r2D.fillIndexBufferWithShapes();
Renderer3D r3D(vulkanInstance, textureManager); /* Renderer3D r3D(vulkanInstance, textureManager); */
/* gz::FontManager fm("fonts"); */ /* gz::vlk::FontManager fm(vulkanInstance, "fonts"); */
/* fm.loadFont("menu.ttf"); */ /* fm.loadFont("menu.ttf"); */
/* /1* fm.getFaces().at("menu.ttf"). *1/ */ /* fm.getFaces().at("menu.ttf"). */
try { /* try */
{
uint32_t imageIndex; uint32_t imageIndex;
/* std::chrono::time_point now = std::chrono::system_clock::now(); */ /* std::chrono::time_point now = std::chrono::system_clock::now(); */
while (! glfwWindowShouldClose(vulkanInstance.getWindow())) { bool first2d = false;
int count = 0;
while (!glfwWindowShouldClose(vulkanInstance.getWindow())) {
glfwPollEvents(); glfwPollEvents();
imageIndex = vulkanInstance.beginFrameDraw(); imageIndex = vulkanInstance.beginFrameDraw();
r2D.drawFrame(imageIndex); uint32_t currentFrame = vulkanInstance.getCurrentFrame();
r3D.drawFrame(imageIndex); r2D.beginFrameDraw(imageIndex, currentFrame);
for (auto& shape : rects) {
shape.draw();
}
if (count > 100) {
/* r2D.drawFrame(imageIndex); */
r2D.endFrameDraw(imageIndex, currentFrame);
/* r3D.drawFrame(imageIndex); */
}
else {
/* r3D.drawFrame(imageIndex); */
r2D.endFrameDraw(imageIndex, currentFrame);
}
count = count < 200 ? ++count : 0;
/* first2d = !first2d; */
vulkanInstance.endFrameDraw(imageIndex); vulkanInstance.endFrameDraw(imageIndex);
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get<uint32_t>("framerate")); auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get<uint32_t>("framerate"));
std::this_thread::sleep_for(SLEEP_TIME); std::this_thread::sleep_for(SLEEP_TIME);
@ -80,10 +94,10 @@ namespace gz::vlk {
} }
vulkanInstance.cleanup(); vulkanInstance.cleanup();
} }
catch (const std::exception& e) { /* catch (const std::exception& e) { */
std::cerr << e.what() << std::endl; /* std::cerr << e.what() << std::endl; */
return 1; /* return 1; */
} /* } */
return 0; return 0;
} }

View File

@ -27,6 +27,7 @@ namespace gz::vlk {
class Renderer { class Renderer {
public: public:
Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {}; Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {};
TextureManager& getTextureManager() { return textureManager; };
protected: protected:
/** /**
* @brief Cleanup vulkan resources held by this class * @brief Cleanup vulkan resources held by this class

View File

@ -1,7 +1,6 @@
#include "renderer2D.hpp" #include "renderer2D.hpp"
#include "exceptions.hpp" #include "exceptions.hpp"
#include "shape.hpp"
#include "vulkan_allocator.hpp" #include "vulkan_allocator.hpp"
#include "vulkan_instance.hpp" #include "vulkan_instance.hpp"
#include "texture_manager.hpp" #include "texture_manager.hpp"
@ -38,7 +37,7 @@ namespace gz::vlk {
// INIT & CLEANUP // INIT & CLEANUP
// //
Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager)
: Renderer(instance, textureManager), shapeBufferManager(instance) : Renderer(instance, textureManager)
{ {
LogCreateInfo logCI { LogCreateInfo logCI {
.logfile = "renderer2D.log", .logfile = "renderer2D.log",
@ -87,6 +86,11 @@ namespace gz::vlk {
// //
// SWAPCHAIN DEPENDANT // SWAPCHAIN DEPENDANT
// //
const vk::Extent2D& Renderer2D::getScExtent() const {
return vk.getScExtent();
}
void Renderer2D::initSwapChainDependantResources() { void Renderer2D::initSwapChainDependantResources() {
createRenderPass(); createRenderPass();
createImageResources(); createImageResources();
@ -311,6 +315,8 @@ namespace gz::vlk {
// RENDERING // RENDERING
// //
void Renderer2D::beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame) { void Renderer2D::beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame) {
this->currentFrame = currentFrame;
this->imageIndex = imageIndex;
commandBuffers[currentFrame].reset(); commandBuffers[currentFrame].reset();
vk::CommandBufferBeginInfo commandBufferBI { vk::CommandBufferBeginInfo commandBufferBI {
/* .flags = 0, */ /* .flags = 0, */
@ -414,48 +420,37 @@ namespace gz::vlk {
/* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */ /* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */
/* } */ /* } */
void Renderer2D::drawShape(BufferManager<Vertex2D, uint32_t>& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex) {
/* rLog.log0("drawShape:", vertexBufferInfo, indexBufferInfo, "textureAtlasIndex:", textureAtlasIndex); */
void Renderer2D::drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame) { auto& cmdBuffer = commandBuffers.at(currentFrame);
/* rLog.log0("drawShape:", shape->getVertexBufferInfo(), shape->getIndexBufferInfo()); */ cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline);
commandBuffers[currentFrame].bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline);
uint32_t descriptorCount = 1; uint32_t descriptorCount = 1;
uint32_t firstSet = 0; uint32_t firstSet = 0;
uint32_t dynamicOffsetCount = 0; uint32_t dynamicOffsetCount = 0;
uint32_t* dynamicOffsets = nullptr; uint32_t* dynamicOffsets = nullptr;
commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets);
vk::Buffer vertexBuffers[] = { bufferManager.getVertexBuffer(vertexBufferInfo) };
vk::Buffer vertexBuffers[] = { shapeBufferManager.getVertexBuffer(shape->getVertexBufferInfo()) }; vk::DeviceSize vertexOffsets[] = { vertexBufferInfo.offset };
vk::DeviceSize vertexOffsets[] = { shape->getVertexBufferInfo().offset };
uint32_t bindingCount = 1; uint32_t bindingCount = 1;
uint32_t binding = 0; uint32_t binding = 0;
commandBuffers[currentFrame].bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets); cmdBuffer.bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets);
vk::Buffer indexBuffers = { shapeBufferManager.getIndexBuffer(shape->getIndexBufferInfo()) }; vk::Buffer indexBuffers = { bufferManager.getIndexBuffer(indexBufferInfo) };
vk::DeviceSize indexOffset = shape->getIndexBufferInfo().offset; vk::DeviceSize indexOffset = indexBufferInfo.offset;
// TODO use correct index type! // TODO use correct index type!
commandBuffers[currentFrame].bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32); cmdBuffer.bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32);
uint32_t pushConstant = shape->getTexureAtlasIndex(); uint32_t pushConstant = textureAtlasIndex;
vk::ShaderStageFlags flags(vk::ShaderStageFlagBits::eFragment); vk::ShaderStageFlags flags(vk::ShaderStageFlagBits::eFragment);
commandBuffers[currentFrame].pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant); cmdBuffer.pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant);
uint32_t indexCount = shape->getIndexBufferInfo().count; uint32_t indexCount = indexBufferInfo.count;
uint32_t instanceCount = 1; uint32_t instanceCount = 1;
uint32_t firstIndex = 0; uint32_t firstIndex = 0;
uint32_t firstInstance = 0; uint32_t firstInstance = 0;
commandBuffers[currentFrame].drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance); cmdBuffer.drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance);
} }
void Renderer2D::initializeShape(Shape* shape) {
// make indices valid
/* shape->setIndexOffset(shapesVerticesCount); */
shape->normalizeVertices(vk.getScExtent().width, vk.getScExtent().height);
shape->setTextureCoordinates(textureManager);
shape->initalizeBufferInfos(shapeBufferManager);
}
} // namespace gz::vlk } // namespace gz::vlk

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include "buffer_manager.hpp"
#include "renderer.hpp" #include "renderer.hpp"
#include "shape.hpp" #include "text.hpp"
#include "vulkan_allocator.hpp" #include "vulkan_allocator.hpp"
#include "vulkan_util.hpp" #include "vulkan_util.hpp"
@ -54,8 +55,8 @@ namespace gz::vlk {
*/ */
void endFrameDraw(uint32_t imageIndex, uint32_t currentFrame); void endFrameDraw(uint32_t imageIndex, uint32_t currentFrame);
void drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame); void drawShape(BufferManager<Vertex2D, uint32_t>& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex);
void initializeShape(Shape* shape); const vk::Extent2D& getScExtent() const;
/** /**
* @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer * @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer
*/ */
@ -64,6 +65,8 @@ namespace gz::vlk {
/// @} /// @}
private: private:
uint32_t imageIndex;
uint32_t currentFrame;
/** /**
* @brief Destroy all vulkan objects owned by this object * @brief Destroy all vulkan objects owned by this object
* @details: * @details:
@ -74,11 +77,6 @@ namespace gz::vlk {
*/ */
void cleanup(); void cleanup();
/* std::vector<Shape> shapes; */
/* size_t shapesVerticesCount = 0; */
/* size_t shapesIndicesCount = 0; */
BufferManager<Vertex2D, uint32_t> shapeBufferManager;
/* void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); */ /* void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); */
/** /**

View File

@ -9,8 +9,7 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <gz-util/exceptions.hpp> #include <gz-util/exceptions.hpp>
#include <gz-util/util/string_concepts.hpp> #include <gz-util/string/conversion.hpp>
#include <gz-util/util/string_conversion.hpp>
#include <ratio> #include <ratio>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -23,8 +22,9 @@ std::string TextureImageArea::toString() const {
// //
// INIT & CLEANUP // INIT & CLEANUP
// //
TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY) TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat)
: vk(instance), : vk(instance),
imageFormat(imageFormat),
slotWidth(slotWidth), slotHeight(slotHeight), slotWidth(slotWidth), slotHeight(slotHeight),
slotCountX(slotCountX), slotCountY(slotCountY) slotCountX(slotCountX), slotCountY(slotCountY)
{ {
@ -59,12 +59,12 @@ void TextureAtlas::cleanup() {
void TextureAtlas::createImageResources() { void TextureAtlas::createImageResources() {
vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eLinear, vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, imageFormat, vk::ImageTiling::eLinear,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal, vk::MemoryPropertyFlagBits::eDeviceLocal,
textureImage, textureImageMemory); textureImage, textureImageMemory);
vk::CommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS); vk::CommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS);
vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer); vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer);
vk::ImageSubresourceRange subresourceRange { vk::ImageSubresourceRange subresourceRange {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0, .baseMipLevel = 0,
@ -73,10 +73,10 @@ void TextureAtlas::createImageResources() {
.layerCount = 1, .layerCount = 1,
}; };
cmdBuffer.clearColorImage(textureImage, vk::ImageLayout::eTransferDstOptimal, &settings::missingTextureColor, 1, &subresourceRange); cmdBuffer.clearColorImage(textureImage, vk::ImageLayout::eTransferDstOptimal, &settings::missingTextureColor, 1, &subresourceRange);
vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer); vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer);
vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS); vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS);
vk.createImageView(vk::Format::eR8G8B8A8Srgb, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor); vk.createImageView(imageFormat, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor);
vk.createTextureSampler(textureSampler); vk.createTextureSampler(textureSampler);
} }
@ -84,7 +84,7 @@ void TextureAtlas::createImageResources() {
// //
// ADD TEXTURE // ADD TEXTURE
// //
std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height) { std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height, uint8_t bytesPerPixel) {
if (freeAreas.empty()) { if (freeAreas.empty()) {
throw Exception("No texture slots available", "TextureAtlas::addTexture"); throw Exception("No texture slots available", "TextureAtlas::addTexture");
} }
@ -151,7 +151,7 @@ std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16
#ifdef LOG_LEVEL_0 #ifdef LOG_LEVEL_0
tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")"); tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")");
#endif #endif
blitTextureOnImage(pixels, x, y, width, height); blitTextureOnImage(pixels, width, height, bytesPerPixel, x, y);
// topleft normalized: slot / slotCount // topleft normalized: slot / slotCount
// bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount // bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount
return { glm::vec2(static_cast<float>(x) / slotCountX, static_cast<float>(y) / slotCountY), return { glm::vec2(static_cast<float>(x) / slotCountX, static_cast<float>(y) / slotCountY),
@ -160,9 +160,8 @@ std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16
} }
void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight) { void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY) {
constexpr size_t BYTES_PER_PIXEL = 4; vk::DeviceSize imageSize = textureWidth * textureHeight * bytesPerPixel;
vk::DeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL;
vk::Buffer stagingBuffer; vk::Buffer stagingBuffer;
MemoryInfo stagingBufferMemory; MemoryInfo stagingBufferMemory;
vk.createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc, vk.createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc,

View File

@ -3,6 +3,7 @@
#include "vulkan_allocator.hpp" #include "vulkan_allocator.hpp"
#include <gz-util/log.hpp> #include <gz-util/log.hpp>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <vulkan/vulkan_enums.hpp>
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
@ -52,16 +53,25 @@ namespace gz::vlk {
* -# @ref createImageResources "create texture image, view and sampler" * -# @ref createImageResources "create texture image, view and sampler"
* *
* The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY * The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY
*
* @param instance Reference to a VulkanInstance
* @param slotWidth Width of a texture slot in pixels
* @param slotHeight Height of a texture slot in pixels
* @param slotCountX Number of slots in a row
* @param slotCountY Number of slots in a column
* @param imageFormat The format of the image. Must match the bytesPerPixel parameter in addTexture()
*/ */
TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY); TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb);
/** /**
* @brief Add a texture to the atlas * @brief Add a texture to the atlas
* @param pixels Buffer containing the bytes. Size of the buffer must be `textureWidth * textureHeight * bytesPerPixel`
* @param textureWidth Width in pixels * @param textureWidth Width in pixels
* @param textureHeight Height in pixels * @param textureHeight Height in pixels
* @param bytesPerPixel Number of bytes per pixel. Must be compatible to the imageFormat passed to the constructor
* @returns The topleft and bottomright texture coordinates in the image of the atlas * @returns The topleft and bottomright texture coordinates in the image of the atlas
* @throws Exception when there is no space in the atlas to add the texture * @throws Exception when there is no space in the atlas to add the texture
*/ */
std::pair<glm::vec2, glm::vec2> addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight); [[ nodiscard ]] std::pair<glm::vec2, glm::vec2> addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel);
const vk::ImageView& getTextureImageView() const { return textureImageView; } const vk::ImageView& getTextureImageView() const { return textureImageView; }
const vk::Sampler& getTextureSampler() const { return textureSampler; } const vk::Sampler& getTextureSampler() const { return textureSampler; }
@ -75,13 +85,14 @@ namespace gz::vlk {
private: private:
VulkanInstance& vk; VulkanInstance& vk;
void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight); void blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY);
/** /**
* @brief Create textureImage, textureImageView and textureSampler * @brief Create textureImage, textureImageView and textureSampler
* @details * @details
* the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout. * the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout.
*/ */
void createImageResources(); void createImageResources();
vk::Format imageFormat;
vk::Image textureImage; vk::Image textureImage;
MemoryInfo textureImageMemory; MemoryInfo textureImageMemory;
vk::ImageView textureImageView; vk::ImageView textureImageView;

View File

@ -1,10 +1,19 @@
#include "texture_manager.hpp" #include "texture_manager.hpp"
#include "exceptions.hpp" #include "exceptions.hpp"
#include "vulkan_instance.hpp" #include "vulkan_instance.hpp"
#include "vulkan_util.hpp"
#include <glm/detail/qualifier.hpp>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <gz-util/exceptions.hpp>
#include <gz-util/string/to_string.hpp>
namespace gz::vlk { namespace gz::vlk {
std::string TextureInfo::toString() const {
return "<atlas: " + gz::toString(atlas) + ", tl: " + gz::toString(texCoordTopLeft) + ", br: " + gz::toString(texCoordBottomRight) + ">";
}
TextureManager::TextureManager(VulkanInstance& instance) TextureManager::TextureManager(VulkanInstance& instance)
: vk(instance) : vk(instance)
{ {
@ -18,7 +27,7 @@ namespace gz::vlk {
tLog = Log(std::move(logCI)); tLog = Log(std::move(logCI));
vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this)); vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this));
atlases.emplace_back(TextureAtlas(vk, 24, 24, 24, 24)); addAtlas(24, 24, 24, 24);
createDescriptorResources(); createDescriptorResources();
VulkanInstance::registerObjectUsingVulkan("TextureManager", VulkanInstance::registerObjectUsingVulkan("TextureManager",
&descriptorSetLayout, &descriptorPool, &descriptorSet); &descriptorSetLayout, &descriptorPool, &descriptorSet);
@ -35,31 +44,75 @@ namespace gz::vlk {
} }
void TextureManager::getTexCoords(const std::string& textureName, glm::vec2& texCoords) { TextureAtlasIndex TextureManager::addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat) {
if (!textureInfos.contains(textureName)) { TextureAtlasIndex index = atlases.size();
loadTextureFromFile(textureName); atlases.emplace_back(TextureAtlas(vk, slotWidth, slotHeight, slotCountX, slotCountY, imageFormat));
// add special "atlas_X" texture
textures.insert( { std::string("atlas_") + gz::toString(index), index });
textureInfos.emplace_back(TextureInfo{ .atlas = index, .texCoordTopLeft = glm::vec2(0, 0), .texCoordBottomRight = glm::vec2(1, 1) });
return index;
}
void TextureManager::isValidTexture(const Texture& texture) const {
if (texture < textureInfos.size()) {
return;
}
else {
throw InvalidArgument("Texture handle '" + gz::toString(texture) + "' is invalid.", "TextureManager::isValidTexture");
} }
TextureInfo& texI = textureInfos[textureName]; }
tLog.log0("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
Texture TextureManager::getTexture(const std::string& textureName) {
if (!textures.contains(textureName)) {
return loadTextureFromFile(textureName);
}
else {
return textures.at(textureName);
}
}
void TextureManager::setTexCoords(const Texture& texture, glm::vec2& texCoords) {
isValidTexture(texture);
TextureInfo& texI = textureInfos.at(texture);
tLog.log0("setTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft); texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft);
} }
void TextureManager::loadTextureFromFile(const std::string& textureName) { //
// ATLASES
//
const TextureAtlas& TextureManager::getTextureAtlas(const Texture& texture) const {
isValidTexture(texture);
return atlases.at(textureInfos.at(texture).atlas);
}
uint32_t TextureManager::getTextureAtlasIndex(const Texture& texture) const {
isValidTexture(texture);
return textureInfos.at(texture).atlas;
}
Texture TextureManager::loadTextureFromFile(const std::string& textureName) {
int textureWidth, textureHeight, textureChannels; int textureWidth, textureHeight, textureChannels;
stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
if (!pixels) { if (!pixels) {
throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture"); throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture");
} }
TextureInfo& texI = textureInfos[textureName]; Texture texture = textureInfos.size();
textureInfos.emplace_back();
TextureInfo& texI = textureInfos.back();
texI.atlas = 0; texI.atlas = 0;
// TODO static constexpr uint8_t BYTES_PER_PIXEL = 4;
auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight); auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight, BYTES_PER_PIXEL);
stbi_image_free(pixels); stbi_image_free(pixels);
tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second); tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second);
texI.texCoordTopLeft = std::move(texCoords.first), texI.texCoordTopLeft = std::move(texCoords.first),
texI.texCoordBottomRight = std::move(texCoords.second), texI.texCoordBottomRight = std::move(texCoords.second),
tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0)); tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0));
tLog.log0("loadTextureFromFile: textureInfos", textureInfos);
return texture;
} }

View File

@ -4,20 +4,34 @@
#include "stb_image.h" #include "stb_image.h"
#include <gz-util/util/string.hpp> #include <gz-util/string/utility.hpp>
#define LOG_SUBLOGS
#include <gz-util/log.hpp> #include <gz-util/log.hpp>
#include <glm/fwd.hpp> #include <glm/fwd.hpp>
#include <unordered_map> #include <unordered_map>
namespace gz::vlk { namespace gz::vlk {
// TODO use this texture handle
using Texture = uint32_t;
using TextureAtlasIndex = uint32_t;
struct TextureInfo { struct TextureInfo {
uint32_t atlas; TextureAtlasIndex atlas;
glm::vec2 texCoordTopLeft; glm::vec2 texCoordTopLeft;
glm::vec2 texCoordBottomRight; glm::vec2 texCoordBottomRight;
std::string toString() const;
}; };
/// Defined in vulkan_instance.hpp /// Defined in vulkan_instance.hpp
class VulkanInstance; class VulkanInstance;
/**
* @details
* Manages multiple @ref TextureAtlas "texture atlases", which each hold one image that contains muiltiple textures
* @section tm_descriptors
* The TextureManager has provides a descriptor set for accessing the texture atlases:
* Bindings:
* -0. Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) of length (getAtlasCount())
*/
class TextureManager { class TextureManager {
public: public:
/** /**
@ -27,48 +41,72 @@ namespace gz::vlk {
* -# @ref createDescriptor "create descriptor set layout, pool and set * -# @ref createDescriptor "create descriptor set layout, pool and set
*/ */
TextureManager(VulkanInstance& instance); TextureManager(VulkanInstance& instance);
void getTexCoords(const std::string& textureName, glm::vec2& texCoords); /**
* @brief Get a texture handle for a texture. Loads the texture from file if not already loaded
*/
Texture getTexture(const std::string& textureName);
/**
* @brief Set the texture coordiantes for the texture
* @details
* @param texCoords
* The texture coordinates to set. texCoords should be from (0,0) to (1,1) and will be
* set to the according texture coordinate of texture.
*/
void setTexCoords(const Texture& texture, glm::vec2& texCoords);
/* void getTexCoords(const std::string& textureName, glm::vec2& texCoords); */
const vk::DescriptorSet& getDescriptorSet() const { return descriptorSet; } const vk::DescriptorSet& getDescriptorSet() const { return descriptorSet; }
const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; } const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; }
// TODO /**
/// @todo take texture as argument * @brief Get the atlas that texture is in
const TextureAtlas& getTextureAtlas() const { return atlases.at(0); } * @throws InvalidArgument if the texture is not present
*/
const TextureAtlas& getTextureAtlas(const Texture& texture) const;
/* const TextureAtlas& getTextureAtlas(const std::string& texture) const; */
/**
* @brief Get the index of the atlas that texture is in
* @throws InvalidArgument if the texture is not present
*/
TextureAtlasIndex getTextureAtlasIndex(const Texture& texture) const;
/* TextureAtlasIndex getTextureAtlasIndex(const std::string& texture) const; */
/**
* @brief Get the number of atlases.
* @details Needed for specialization constant in shaders using sampler arrays
*/
uint32_t getAtlasCount() const { return static_cast<uint32_t>(atlases.size()); } uint32_t getAtlasCount() const { return static_cast<uint32_t>(atlases.size()); }
/**
* @brief Add a new atlas and a special "atlas_X" texture (where X is the returned index)
*/
TextureAtlasIndex addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb);
private: private:
void cleanup(); void cleanup();
void loadTextureFromFile(const std::string& textureName); /**
// TODO index currently has no meaning * @brief Check if texture handle is valid
* @throws InvalidArgument if texture is not valid
*/
void isValidTexture(const Texture& texture) const;
Texture loadTextureFromFile(const std::string& textureName);
std::vector<TextureAtlas> atlases; std::vector<TextureAtlas> atlases;
/** /**
* @brief Contains textureName : TextureInfo pairs * @brief Contains textureName : Texture pairs
*/ */
util::unordered_string_map<TextureInfo> textureInfos; util::unordered_string_map<Texture> textures;
std::vector<TextureInfo> textureInfos;
VulkanInstance& vk; VulkanInstance& vk;
Log tLog; Log tLog;
/** /**
* @name Create desciptors * @name Create desciptors
* @details These functions create a desciptor with bindings for * @see tm_descriptors
* -# Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) at binding 0. Array length is atlas count */
* @{ /// @{
*/
vk::DescriptorSetLayout descriptorSetLayout; vk::DescriptorSetLayout descriptorSetLayout;
vk::DescriptorPool descriptorPool; vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet; vk::DescriptorSet descriptorSet;
/* std::vector<vk::DescriptorSet> descriptorSets; */
/**
* @brief Create a descriptor set, layout and pool
* @details
* Bindings:
* -0. texture sampler array COMBINED_IMAGE_SAMPLER[atlasCount]
*/
void createDescriptorResources(); void createDescriptorResources();
/* const uint32_t bindingCombinedImageSampler = 1; */ /// @}
/**
* @}
*/
}; };

View File

@ -2,7 +2,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include <gz-util/util/string_conversion.hpp> #include <gz-util/string/to_string.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>