first draft of vulkan texture impl

This commit is contained in:
niki
2022-05-06 16:06:09 +02:00
parent 8f55f093a4
commit 4e8304255d
5 changed files with 296 additions and 18 deletions
+2 -3
View File
@@ -46,8 +46,8 @@ public:
bool loadVolatile() override;
void unloadVolatile() override;
void copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
void copyToBuffer(love::graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override;
void copyFromBuffer(love::graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override;
void copyToBuffer(love::graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override;
void setSamplerState(const SamplerState &s) override;
@@ -59,7 +59,6 @@ public:
inline GLuint getFBO() const { return fbo; }
private:
void createTexture();
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
+46 -9
View File
@@ -94,7 +94,7 @@ namespace love {
void Graphics::startRecordingGraphicsCommands() {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
while (true) {
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
@@ -211,7 +211,7 @@ namespace love {
recreateSwapChain();
}
bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) {
bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) {
std::cout << "setMode ";
initCapabilities();
@@ -232,9 +232,6 @@ namespace love {
void Graphics::initCapabilities() {
std::cout << "initCapabilities ";
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
// todo
capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false;
@@ -256,6 +253,9 @@ namespace love {
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false;
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
capabilities.limits[LIMIT_POINT_SIZE] = properties.limits.pointSizeRange[1];
capabilities.limits[LIMIT_TEXTURE_SIZE] = properties.limits.maxImageDimension2D;
capabilities.limits[LIMIT_TEXTURE_LAYERS] = properties.limits.maxImageArrayLayers;
@@ -283,27 +283,59 @@ namespace love {
std::cout << "unSetMode ";
}
void Graphics::draw(const DrawIndexedCommand& cmd) {
void Graphics::draw(const DrawIndexedCommand& cmd) {
std::cout << "drawIndexed ";
std::vector<VkBuffer> buffers;
std::vector<VkDeviceSize> offsets;
buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle());
offsets.push_back((VkDeviceSize) 0);
offsets.push_back((VkDeviceSize)0);
vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.at(currentFrame), 0, nullptr);
vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, 1, buffers.data(), offsets.data());
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer) cmd.indexBuffer->getHandle(), 0, VK_INDEX_TYPE_UINT16);
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), 0, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.indexCount), 1, 0, 0, 0);
}
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
std::cout << "newStreamBuffer ";
return new StreamBuffer(vmaAllocator, type, size);
}
// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
VkCommandBuffer Graphics::beginSingleTimeCommands() {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = commandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
void Graphics::endSingleTimeCommands(VkCommandBuffer commandBuffer) {
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(graphicsQueue);
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
}
void Graphics::prepareDraw(uint32_t currentImage) {
auto& buffer = uniformBuffers.at(currentImage);
@@ -471,6 +503,10 @@ namespace love {
}
}
if (!deviceFeatures.samplerAnisotropy) {
score = 0;
}
return score;
}
@@ -523,6 +559,7 @@ namespace love {
}
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+10 -1
View File
@@ -37,8 +37,14 @@ namespace love {
return physicalDevice;
}
const VmaAllocator getVmaAllocator() const {
return vmaAllocator;
}
// implementation for virtual functions
Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { std::cout << "newTexture "; return new Texture(this, settings, data); }
love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override {
return new Texture(this, settings, data);
}
love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) override;
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear1 "; }
void clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear2 "; }
@@ -70,6 +76,9 @@ namespace love {
void draw(const DrawIndexedCommand& cmd) override;
void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override { std::cout << "drawQuads "; }
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer);
protected:
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
std::cout << "newShaderStageInternal ";
+207 -3
View File
@@ -1,15 +1,219 @@
#include "Texture.h"
#include "Graphics.h"
// make vulkan::Graphics functions available
#define vgfx ((Graphics*)gfx)
namespace love {
namespace graphics {
namespace vulkan {
Texture::Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data)
: love::graphics::Texture(gfx, settings, data) {
: love::graphics::Texture(gfx, settings, data), slices(settings.type), gfx(gfx) {
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
if (data != nullptr) {
slices = *data;
}
loadVolatile();
}
Texture::~Texture() {
unloadVolatile();
}
bool Texture::loadVolatile() {
auto data = slices.get(0, 0);
auto size = data->getSize();
auto dataPtr = data->getData();
Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = data->getWidth();
rect.h = data->getHeight();
uploadByteData(PIXELFORMAT_BGRA8_UNORM, dataPtr, size, 0, 0, rect);
createTextureImageView();
createTextureSampler();
return true;
}
void Texture::unloadVolatile() {
// vkDestroySampler(device, textureSampler, nullptr);
// vkDestroyImageView(device, textureImageView, nullptr);
// vmaDestroyImage(allocator, textureImage, nullptr);
}
void Texture::createTextureImageView() {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.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(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
throw love::Exception("could not create texture image view");
}
}
void Texture::createTextureSampler() {
auto physicalDevice = vgfx->getPhysicalDevice();
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
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_TRUE;
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
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(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
throw love::Exception("failed to create texture sampler");
}
}
void Texture::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
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;
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 love::Exception("unsupported lay transition");
}
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
vgfx->endSingleTimeCommands(commandBuffer);
}
void Texture::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = {
width,
height, 1
};
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
vgfx->endSingleTimeCommands(commandBuffer);
}
void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) {
std::cout << "Texture::uploadByteData ";
VkBuffer stagingBuffer;
VmaAllocation vmaAllocation;
VkBufferCreateInfo bufferCreateInfo{};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCreateInfo.size = size;
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo);
memcpy(allocInfo.pMappedData, data, size);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<uint32_t>(r.w);
imageInfo.extent.height = static_cast<uint32_t>(r.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;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
throw love::Exception("failed to create image");
}
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(r.w), static_cast<uint32_t>(r.h));
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
}
}
}
}
}
+31 -2
View File
@@ -1,13 +1,25 @@
#ifndef LOVE_GRAPHICS_VULKAN_TEXTURE_H
#define LOVE_GRAPHICS_VULKAN_TEXTURE_H
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "vk_mem_alloc.h"
#include <iostream>
namespace love {
namespace graphics {
namespace vulkan {
class Texture : public graphics::Texture {
class Texture : public graphics::Texture, public graphics::Volatile {
public:
Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data);
~Texture();
bool loadVolatile() override;
void unloadVolatile() override;
void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { std::cout << "Texture::copyFromBuffer "; };
void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { std::cout << "Texture::copyToBuffer "; };
@@ -22,7 +34,24 @@ namespace love {
int getMSAA() const override { std::cout << "Texture::getMSAA "; return 0; };
ptrdiff_t getHandle() const override { std::cout << "Texture::getHandle "; return (ptrdiff_t)0; }
private:
void transitionImageLayout(VkImage, VkFormat, VkImageLayout oldLayout, VkImageLayout newLayout);
void copyBufferToImage(VkBuffer, VkImage, uint32_t width, uint32_t height);
void createTextureImageView();
void createTextureSampler();
graphics::Graphics* gfx;
VkDevice device;
Slices slices;
VmaAllocator allocator;
VkImage textureImage;
VmaAllocation textureImageAllocation;
VkImageView textureImageView;
VkSampler textureSampler;
};
}
}
}
}
#endif