mirror of
https://github.com/bailwillharr/engine.git
synced 2024-09-21 04:51:18 +00:00
Support textures with vulkan; add FPS limiter.
This commit is contained in:
parent
d4106091cd
commit
7cc09484c1
@ -6,6 +6,7 @@
|
||||
|
||||
#include "resources/shader.hpp"
|
||||
#include "resources/mesh.hpp"
|
||||
#include "resources/texture.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@ -26,7 +27,7 @@ public:
|
||||
void setTexture(const std::string& name);
|
||||
|
||||
std::shared_ptr<resources::Mesh> m_mesh = nullptr;
|
||||
// std::shared_ptr<resources::Texture> m_texture;
|
||||
std::shared_ptr<resources::Texture> m_texture;
|
||||
|
||||
glm::vec3 m_color = { 1.0f, 1.0f, 1.0f };
|
||||
glm::vec3 m_emission = { 0.0f, 0.0f, 0.0f };
|
||||
|
@ -48,5 +48,6 @@ namespace engine::gfx {
|
||||
// handles (incomplete types)
|
||||
struct Pipeline;
|
||||
struct Buffer;
|
||||
struct Texture;
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ namespace engine {
|
||||
gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data);
|
||||
void destroyBuffer(const gfx::Buffer* buffer);
|
||||
|
||||
gfx::Texture* createTexture(const void* imageData, uint32_t w, uint32_t h);
|
||||
void destroyTexture(const gfx::Texture* texture);
|
||||
|
||||
// wait until all the active GPU queues have finished working
|
||||
void waitIdle();
|
||||
|
||||
|
@ -1,27 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#if 0
|
||||
|
||||
#include "engine_api.h"
|
||||
|
||||
#include "resource.hpp"
|
||||
|
||||
#include "gfx_device.hpp"
|
||||
|
||||
namespace engine::resources {
|
||||
|
||||
class ENGINE_API Texture : public Resource {
|
||||
|
||||
private:
|
||||
|
||||
public:
|
||||
Texture(const std::filesystem::path& resPath);
|
||||
~Texture() override;
|
||||
|
||||
void bindTexture() const;
|
||||
|
||||
static void invalidate();
|
||||
private:
|
||||
gfx::Texture* m_gpuTexture;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
@ -42,7 +42,7 @@ void Renderer::setMesh(const std::string& name)
|
||||
|
||||
void Renderer::setTexture(const std::string& name)
|
||||
{
|
||||
// m_texture = parent.res.get<resources::Texture>(name);
|
||||
m_texture = parent.res.get<resources::Texture>(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,9 @@
|
||||
#include "components/mesh_renderer.hpp"
|
||||
#include "components/camera.hpp"
|
||||
|
||||
// To allow the FPS-limiter to put the thread to sleep
|
||||
#include <thread>
|
||||
|
||||
namespace engine {
|
||||
|
||||
Application::Application(const char* appName, const char* appVersion)
|
||||
@ -51,6 +54,12 @@ namespace engine {
|
||||
uint64_t lastTick = m_win->getNanos();
|
||||
constexpr int TICKFREQ = 1; // in hz
|
||||
|
||||
constexpr int FPS_LIMIT = 240;
|
||||
constexpr auto FRAMETIME_LIMIT = std::chrono::nanoseconds(1000000000 / FPS_LIMIT);
|
||||
|
||||
auto beginFrame = std::chrono::steady_clock::now();
|
||||
auto endFrame = beginFrame + FRAMETIME_LIMIT;
|
||||
|
||||
// single-threaded game loop
|
||||
while (m_win->isRunning()) {
|
||||
|
||||
@ -79,6 +88,12 @@ namespace engine {
|
||||
/* poll events */
|
||||
m_win->getInputAndEvents();
|
||||
|
||||
/* fps limiter */
|
||||
std::this_thread::sleep_until(endFrame);
|
||||
|
||||
beginFrame = endFrame;
|
||||
endFrame = beginFrame + FRAMETIME_LIMIT;
|
||||
|
||||
}
|
||||
|
||||
gfxdev->waitIdle();
|
||||
|
@ -65,12 +65,6 @@ namespace engine {
|
||||
VkImageView view;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
VkImage image;
|
||||
VmaAllocation allocation;
|
||||
// TODO
|
||||
};
|
||||
|
||||
struct Swapchain {
|
||||
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||
|
||||
@ -122,6 +116,11 @@ namespace engine {
|
||||
std::array<VkDescriptorSet, FRAMES_IN_FLIGHT> descriptorSets{};
|
||||
};
|
||||
|
||||
struct gfx::Texture {
|
||||
VkImage image;
|
||||
VmaAllocation alloc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// enum converters
|
||||
@ -735,6 +734,86 @@ namespace engine {
|
||||
|
||||
}
|
||||
|
||||
VkCommandBuffer beginOneTimeCommands(VkDevice device, VkCommandPool commandPool, VkQueue queue)
|
||||
{
|
||||
VkResult res;
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandPool = commandPool;
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer commandBuffer;
|
||||
res = vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
|
||||
res = vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
static void endOneTimeCommands(VkDevice device, VkCommandPool commandPool, VkCommandBuffer commandBuffer, VkQueue queue)
|
||||
{
|
||||
VkResult res;
|
||||
res = vkEndCommandBuffer(commandBuffer);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
VkSubmitInfo submitInfo{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
|
||||
res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
assert(res == VK_SUCCESS);
|
||||
res = vkQueueWaitIdle(queue);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||
}
|
||||
|
||||
static void cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImageLayout oldLayout, VkImageLayout newLayout, VkImage image)
|
||||
{
|
||||
|
||||
VkImageMemoryBarrier barrier{ 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);
|
||||
|
||||
}
|
||||
|
||||
// class definitions
|
||||
|
||||
struct GFXDevice::Impl {
|
||||
@ -1723,6 +1802,102 @@ namespace engine {
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
gfx::Texture* GFXDevice::createTexture(const void* imageData, uint32_t w, uint32_t h)
|
||||
{
|
||||
auto out = new gfx::Texture;
|
||||
|
||||
VkResult res;
|
||||
|
||||
size_t imageSize = w * h * 4;
|
||||
|
||||
// first load image into staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VmaAllocation stagingAllocation;
|
||||
{
|
||||
VkBufferCreateInfo stagingBufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
stagingBufferInfo.size = imageSize;
|
||||
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
stagingBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
stagingBufferInfo.flags = 0;
|
||||
|
||||
VmaAllocationCreateInfo stagingAllocInfo{};
|
||||
stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||
stagingAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
res = vmaCreateBuffer(pimpl->allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
void* dataDest;
|
||||
res = vmaMapMemory(pimpl->allocator, stagingAllocation, &dataDest);
|
||||
assert(res == VK_SUCCESS);
|
||||
memcpy(dataDest, imageData, imageSize);
|
||||
vmaUnmapMemory(pimpl->allocator, stagingAllocation);
|
||||
}
|
||||
|
||||
// create the image
|
||||
VkImageCreateInfo imageInfo{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent.width = w;
|
||||
imageInfo.extent.height = h;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.flags = 0;
|
||||
|
||||
VmaAllocationCreateInfo imageAllocInfo{};
|
||||
imageAllocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
|
||||
res = vmaCreateImage(pimpl->allocator, &imageInfo, &imageAllocInfo, &out->image, &out->alloc, nullptr);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
// transition the image layout
|
||||
{
|
||||
VkCommandBuffer commandBuffer = beginOneTimeCommands(pimpl->device, pimpl->commandPool, pimpl->gfxQueue.handle);
|
||||
|
||||
// begin cmd buffer
|
||||
|
||||
cmdTransitionImageLayout(commandBuffer, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, out->image);
|
||||
|
||||
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 = w;
|
||||
region.imageExtent.height = h;
|
||||
region.imageExtent.depth = 1;
|
||||
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, out->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
cmdTransitionImageLayout(commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, out->image);
|
||||
|
||||
// end cmd buffer
|
||||
endOneTimeCommands(pimpl->device, pimpl->commandPool, commandBuffer, pimpl->gfxQueue.handle);
|
||||
|
||||
}
|
||||
|
||||
// destroy staging buffer
|
||||
vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void GFXDevice::destroyTexture(const gfx::Texture* texture)
|
||||
{
|
||||
vmaDestroyImage(pimpl->allocator, texture->image, texture->alloc);
|
||||
}
|
||||
|
||||
void GFXDevice::waitIdle()
|
||||
{
|
||||
vkDeviceWaitIdle(pimpl->device);
|
||||
|
@ -1,5 +1,3 @@
|
||||
#if 0
|
||||
|
||||
#include "resources/texture.hpp"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
@ -11,37 +9,25 @@
|
||||
|
||||
namespace engine::resources {
|
||||
|
||||
// -1 means invalid / no bind
|
||||
GLuint Texture::s_binded_texture = -1;
|
||||
|
||||
void Texture::invalidate()
|
||||
{
|
||||
s_binded_texture = -1;
|
||||
}
|
||||
|
||||
// returns false if unable to open file
|
||||
static bool readPNG(const std::string& path, std::vector<uint8_t>& texbuf, int *width, int *height, bool *isRGBA)
|
||||
{
|
||||
int x, y, n;
|
||||
unsigned char *data = stbi_load(path.c_str(), &x, &y, &n, 0);
|
||||
unsigned char *data = stbi_load(path.c_str(), &x, &y, &n, STBI_rgb_alpha);
|
||||
|
||||
|
||||
if (data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t size = (size_t)x * (size_t)y * (size_t)n;
|
||||
const size_t size = (size_t)x * (size_t)y * 4;
|
||||
|
||||
texbuf.resize(size);
|
||||
memcpy(texbuf.data(), data, size);
|
||||
|
||||
*width = x;
|
||||
*height = y;
|
||||
if (n == 4) {
|
||||
*isRGBA = true;
|
||||
} else {
|
||||
*isRGBA = false;
|
||||
}
|
||||
*isRGBA = true;
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
@ -94,22 +80,12 @@ Texture::Texture(const std::filesystem::path& resPath) : Resource(resPath, "text
|
||||
if (!success) {
|
||||
throw std::runtime_error("Unable to open texture: " + resPath.string());
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
|
||||
bindTexture(); // glBindTexture
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
if (isRGBA) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texbuf.data());
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texbuf.data());
|
||||
|
||||
if (isRGBA == false) {
|
||||
throw std::runtime_error("Currently, only RGBA textures are supported. Size: " + std::to_string(texbuf.size()));
|
||||
}
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
m_gpuTexture = gfxdev->createTexture(texbuf.data(), (uint32_t)width, (uint32_t)height);
|
||||
|
||||
DEBUG("loaded texture {} width: {} height: {} size: {}", resPath.filename().string(), width, height, texbuf.size());
|
||||
|
||||
@ -117,18 +93,7 @@ Texture::Texture(const std::filesystem::path& resPath) : Resource(resPath, "text
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (s_binded_texture == m_texture) {
|
||||
s_binded_texture = -1;
|
||||
}
|
||||
gfxdev->destroyTexture(m_gpuTexture);
|
||||
}
|
||||
|
||||
void Texture::bindTexture() const
|
||||
{
|
||||
if (s_binded_texture != m_texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, m_texture);
|
||||
s_binded_texture = m_texture;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
@ -291,7 +291,7 @@ namespace engine {
|
||||
|
||||
void Window::toggleFullscreen()
|
||||
{
|
||||
setFullscreen(!m_fullscreen, true);
|
||||
setFullscreen(!m_fullscreen, false);
|
||||
}
|
||||
|
||||
bool Window::isFullscreen() const
|
||||
|
@ -16,7 +16,7 @@ void main() {
|
||||
vec3 lightColor = vec3(1.0, 1.0, 1.0);
|
||||
vec3 ambientColor = vec3(1.0, 1.0, 1.0);
|
||||
float ambientStrength = 0.1;
|
||||
vec3 baseColor = vec3(fragColor.x * snoise(fragUV * 10.0), fragColor.yz);
|
||||
vec3 baseColor = vec3(fragColor.x * snoise(fragUV * 10.0), mod(fragUV.x, 1.0), mod(fragUV.y, 1.0));
|
||||
vec3 emission = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
// code
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include "components/camera.hpp"
|
||||
#include "components/mesh_renderer.hpp"
|
||||
|
||||
#include "resource_manager.hpp"
|
||||
#include "resources/texture.hpp"
|
||||
|
||||
#include "camera_controller.hpp"
|
||||
#include "meshgen.hpp"
|
||||
|
||||
@ -16,6 +19,8 @@
|
||||
|
||||
#include <log.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
void playGame()
|
||||
{
|
||||
engine::Application app(PROJECT_NAME, PROJECT_VERSION);
|
||||
@ -115,8 +120,13 @@ void playGame()
|
||||
};
|
||||
app.scene()->getChild("cube")->createComponent<Spin>();
|
||||
|
||||
auto sphere = app.scene()->createChild("sphere");
|
||||
|
||||
app.scene()->printTree();
|
||||
sphere->transform.position = { 10.0f, 5.0f, 10.0f };
|
||||
|
||||
auto sphereRenderer = sphere->createComponent<engine::components::Renderer>();
|
||||
sphereRenderer->m_mesh = genSphereMesh(5.0f, 100, false);
|
||||
sphereRenderer->setTexture("textures/cobble_stone.png");
|
||||
|
||||
app.gameLoop();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user