vulkan: remove redundant tabs

this makes reading the source files easier
and is more in line with the rest of the löve codebase
This commit is contained in:
niki
2022-07-30 01:16:56 +02:00
parent 506cf8c210
commit 7bbc042fce
14 changed files with 3431 additions and 3425 deletions
+85 -84
View File
@@ -2,91 +2,92 @@
#include "Graphics.h"
namespace love {
namespace graphics {
namespace vulkan {
static VkBufferUsageFlags getUsageBit(BufferUsage mode) {
switch (mode) {
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
namespace graphics {
namespace vulkan {
static VkBufferUsageFlags getVulkanUsageFlags(BufferUsageFlags flags) {
VkBufferUsageFlags vkFlags = 0;
for (int i = 0; i < BUFFERUSAGE_MAX_ENUM; i++) {
BufferUsageFlags flag = static_cast<BufferUsageFlags>(1u << i);
if (flags & flag) {
vkFlags |= getUsageBit((BufferUsage)i);
}
}
return vkFlags;
}
static VkBufferUsageFlags getUsageBit(BufferUsage mode) {
switch (mode) {
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength)
: love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), gfx(gfx) {
loadVolatile();
}
bool Buffer::loadVolatile() {
Graphics* vgfx = (Graphics*)gfx;
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
return true;
}
void Buffer::unloadVolatile() {
if (buffer == VK_NULL_HANDLE)
return;
Graphics* vgfx = (Graphics*)gfx;
auto device = vgfx->getDevice();
vgfx->queueCleanUp(
[device=device, allocator=allocator, buffer=buffer, allocation=allocation](){
vkDeviceWaitIdle(device);
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
Buffer::~Buffer() {
unloadVolatile();
}
void* Buffer::map(MapType map, size_t offset, size_t size) {
char* data = (char*)allocInfo.pMappedData;
return (void*) (data + offset);
}
bool Buffer::fill(size_t offset, size_t size, const void *data) {
void* dst = (void*)((char*)allocInfo.pMappedData + offset);
memcpy(dst, data, size);
return true;
}
void Buffer::unmap(size_t usedoffset, size_t usedsize) {
(void)usedoffset;
(void)usedsize;
}
void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) {
throw love::Exception("not implemented yet");
}
static VkBufferUsageFlags getVulkanUsageFlags(BufferUsageFlags flags) {
VkBufferUsageFlags vkFlags = 0;
for (int i = 0; i < BUFFERUSAGE_MAX_ENUM; i++) {
BufferUsageFlags flag = static_cast<BufferUsageFlags>(1u << i);
if (flags & flag) {
vkFlags |= getUsageBit((BufferUsage)i);
}
}
}
return vkFlags;
}
Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength)
: love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), gfx(gfx) {
loadVolatile();
}
bool Buffer::loadVolatile() {
Graphics* vgfx = (Graphics*)gfx;
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
return true;
}
void Buffer::unloadVolatile() {
if (buffer == VK_NULL_HANDLE)
return;
Graphics* vgfx = (Graphics*)gfx;
auto device = vgfx->getDevice();
vgfx->queueCleanUp(
[device=device, allocator=allocator, buffer=buffer, allocation=allocation](){
vkDeviceWaitIdle(device);
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
Buffer::~Buffer() {
unloadVolatile();
}
void* Buffer::map(MapType map, size_t offset, size_t size) {
char* data = (char*)allocInfo.pMappedData;
return (void*) (data + offset);
}
bool Buffer::fill(size_t offset, size_t size, const void *data) {
void* dst = (void*)((char*)allocInfo.pMappedData + offset);
memcpy(dst, data, size);
return true;
}
void Buffer::unmap(size_t usedoffset, size_t usedsize) {
(void)usedoffset;
(void)usedsize;
}
void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) {
throw love::Exception("not implemented yet");
}
} // vulkan
} // graphics
} // love
+36 -31
View File
@@ -1,3 +1,6 @@
#ifndef LOVE_GRAPHICS_VULKAN_BUFFER_H
#define LOVE_GRAPHICS_VULKAN_BUFFER_H
#include "graphics/Buffer.h"
#include <vulkan/vulkan.h>
#include "vk_mem_alloc.h"
@@ -5,37 +8,39 @@
namespace love {
namespace graphics {
namespace vulkan {
class Buffer : public love::graphics::Buffer, public Volatile {
public:
Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength);
virtual ~Buffer();
namespace graphics {
namespace vulkan {
class Buffer : public love::graphics::Buffer, public Volatile {
public:
Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector<DataDeclaration>& format, const void* data, size_t size, size_t arraylength);
virtual ~Buffer();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
void* map(MapType map, size_t offset, size_t size) override;
void unmap(size_t usedoffset, size_t usedsize) override;
bool fill(size_t offset, size_t size, const void* data) override;
void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override;
ptrdiff_t getHandle() const override {
return (ptrdiff_t) buffer; // todo ?
}
ptrdiff_t getTexelBufferHandle() const override {
throw love::Exception("unimplemented Buffer::getTexelBufferHandle");
return (ptrdiff_t) nullptr; // todo ?
}
private:
// todo use a staging buffer for improved performance
VkBuffer buffer = VK_NULL_HANDLE;
love::graphics::Graphics* gfx;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
BufferUsageFlags usageFlags;
};
}
void* map(MapType map, size_t offset, size_t size) override;
void unmap(size_t usedoffset, size_t usedsize) override;
bool fill(size_t offset, size_t size, const void* data) override;
void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override;
ptrdiff_t getHandle() const override {
return (ptrdiff_t) buffer; // todo ?
}
}
ptrdiff_t getTexelBufferHandle() const override {
throw love::Exception("unimplemented Buffer::getTexelBufferHandle");
return (ptrdiff_t) nullptr; // todo ?
}
private:
// todo use a staging buffer for improved performance
VkBuffer buffer = VK_NULL_HANDLE;
love::graphics::Graphics* gfx;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
BufferUsageFlags usageFlags;
};
} // vulkan
} // graphics
} // love
#endif
File diff suppressed because it is too large Load Diff
+200 -200
View File
@@ -18,209 +18,209 @@
namespace love {
namespace graphics {
namespace vulkan {
struct GraphicsPipelineConfiguration {
std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
Shader* shader = nullptr;
PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM;
VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL;
BlendState blendState;
ColorChannelMask colorChannelMask;
Winding winding;
CullMode cullmode;
VkFormat framebufferFormat;
float viewportWidth;
float viewportHeight;
std::optional<Rect> scissorRect;
namespace graphics {
namespace vulkan {
struct GraphicsPipelineConfiguration {
std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
Shader* shader = nullptr;
PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM;
VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL;
BlendState blendState;
ColorChannelMask colorChannelMask;
Winding winding;
CullMode cullmode;
VkFormat framebufferFormat;
float viewportWidth;
float viewportHeight;
std::optional<Rect> scissorRect;
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
};
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
};
struct BatchedDrawBuffers {
StreamBuffer* vertexBuffer1;
StreamBuffer* vertexBuffer2;
StreamBuffer* indexBuffer;
StreamBuffer* constantColorBuffer;
struct BatchedDrawBuffers {
StreamBuffer* vertexBuffer1;
StreamBuffer* vertexBuffer2;
StreamBuffer* indexBuffer;
StreamBuffer* constantColorBuffer;
~BatchedDrawBuffers() {
delete vertexBuffer1;
delete vertexBuffer2;
delete indexBuffer;
delete constantColorBuffer;
}
};
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities{};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
class Graphics final : public love::graphics::Graphics {
public:
Graphics() = default;
virtual ~Graphics();
const char* getName() const override;
const VkDevice getDevice() const;
const VkPhysicalDevice getPhysicalDevice() const;
const VmaAllocator getVmaAllocator() const;
// implementation for virtual functions
love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override;
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;
void clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) override;
Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override;
void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override { }
void present(void* screenshotCallbackdata) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
int getRequestedBackbufferMSAA() const override { return 0; }
int getBackbufferMSAA() const override { return 0; }
void setColor(Colorf c) override;
void setScissor(const Rect& rect) override;
void setScissor() override;
void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { }
void setDepthMode(CompareMode compare, bool write) override { }
void setFrontFaceWinding(Winding winding) override;
void setColorMask(ColorChannelMask mask) override;
void setBlendState(const BlendState& blend) override;
void setPointSize(float size) override;
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
void draw(const DrawCommand& cmd) override;
void draw(const DrawIndexedCommand& cmd) override;
void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override;
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; };
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; }
void queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
void queueCleanUp(std::function<void()> cleanUp);
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer);
uint32_t getNumImagesInFlight() const;
const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
graphics::Texture* getDefaultTexture() const;
protected:
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
return new ShaderStage(this, stage, source, gles, cachekey);
}
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override {
return new Shader(stages);
}
graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override { return false; }
void initCapabilities() override;
void getAPIStats(int& shaderswitches) const override;
void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
private:
void createVulkanInstance();
bool checkValidationSupport();
void pickPhysicalDevice();
int rateDeviceSuitability(VkPhysicalDevice device);
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
void createLogicalDevice();
void initVMA();
void createSurface();
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
void createSwapChain();
void createImageViews();
void createDefaultShaders();
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
void createCommandPool();
void createCommandBuffers();
void createSyncObjects();
void createDefaultTexture();
void createQuadIndexBuffer();
void cleanup();
void cleanupSwapChain();
void recreateSwapChain();
void startRecordingGraphicsCommands();
void endRecordingGraphicsCommands();
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
void updatedBatchedDrawBuffers();
void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
void startRenderPass(Texture*, uint32_t w, uint32_t h);
void endRenderPass();
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
VkExtent2D swapChainExtent = VkExtent2D();
std::vector<VkImageView> swapChainImageViews;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> dataTransferCommandBuffers;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment;
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
std::unique_ptr<Texture> standardTexture = nullptr;
std::unique_ptr<StreamBuffer> quadIndexBuffer = nullptr;
// we need an array of draw buffers, since the frames are being rendered asynchronously
// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
std::vector<BatchedDrawBuffers> batchedDrawBuffers;
// functions that need to be called to cleanup objects that were needed for rendering a frame.
// just like batchedDrawBuffers we need a vector for each frame in flight.
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
graphics::Texture* currentTexture = nullptr;
VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
// render pass variables.
VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED;
Texture* renderTargetTexture;
float currentViewportWidth = 0;
float currentViewportHeight = 0;
};
}
~BatchedDrawBuffers() {
delete vertexBuffer1;
delete vertexBuffer2;
delete indexBuffer;
delete constantColorBuffer;
}
}
};
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities{};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
class Graphics final : public love::graphics::Graphics {
public:
Graphics() = default;
virtual ~Graphics();
const char* getName() const override;
const VkDevice getDevice() const;
const VkPhysicalDevice getPhysicalDevice() const;
const VmaAllocator getVmaAllocator() const;
// implementation for virtual functions
love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override;
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;
void clear(const std::vector<OptionalColorD>& colors, OptionalInt stencil, OptionalDouble depth) override;
Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override;
void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override { }
void present(void* screenshotCallbackdata) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
int getRequestedBackbufferMSAA() const override { return 0; }
int getBackbufferMSAA() const override { return 0; }
void setColor(Colorf c) override;
void setScissor(const Rect& rect) override;
void setScissor() override;
void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { }
void setDepthMode(CompareMode compare, bool write) override { }
void setFrontFaceWinding(Winding winding) override;
void setColorMask(ColorChannelMask mask) override;
void setBlendState(const BlendState& blend) override;
void setPointSize(float size) override;
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
void draw(const DrawCommand& cmd) override;
void draw(const DrawIndexedCommand& cmd) override;
void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override;
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; };
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; }
void queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
void queueCleanUp(std::function<void()> cleanUp);
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer);
uint32_t getNumImagesInFlight() const;
const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
graphics::Texture* getDefaultTexture() const;
protected:
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
return new ShaderStage(this, stage, source, gles, cachekey);
}
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override {
return new Shader(stages);
}
graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override { return false; }
void initCapabilities() override;
void getAPIStats(int& shaderswitches) const override;
void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
private:
void createVulkanInstance();
bool checkValidationSupport();
void pickPhysicalDevice();
int rateDeviceSuitability(VkPhysicalDevice device);
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
void createLogicalDevice();
void initVMA();
void createSurface();
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
void createSwapChain();
void createImageViews();
void createDefaultShaders();
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
void createCommandPool();
void createCommandBuffers();
void createSyncObjects();
void createDefaultTexture();
void createQuadIndexBuffer();
void cleanup();
void cleanupSwapChain();
void recreateSwapChain();
void startRecordingGraphicsCommands();
void endRecordingGraphicsCommands();
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
void updatedBatchedDrawBuffers();
void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
void startRenderPass(Texture*, uint32_t w, uint32_t h);
void endRenderPass();
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
VkExtent2D swapChainExtent = VkExtent2D();
std::vector<VkImageView> swapChainImageViews;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> dataTransferCommandBuffers;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment;
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
std::unique_ptr<Texture> standardTexture = nullptr;
std::unique_ptr<StreamBuffer> quadIndexBuffer = nullptr;
// we need an array of draw buffers, since the frames are being rendered asynchronously
// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
std::vector<BatchedDrawBuffers> batchedDrawBuffers;
// functions that need to be called to cleanup objects that were needed for rendering a frame.
// just like batchedDrawBuffers we need a vector for each frame in flight.
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
graphics::Texture* currentTexture = nullptr;
VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
// render pass variables.
VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED;
Texture* renderTargetTexture;
float currentViewportWidth = 0;
float currentViewportHeight = 0;
};
} // vulkan
} // graphics
} // love
#endif
+478 -478
View File
@@ -8,486 +8,486 @@
#include <vector>
namespace love {
namespace graphics {
namespace vulkan {
static const TBuiltInResource defaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
/* .MaxTextureUnits = */ 32,
/* .MaxTextureCoords = */ 32,
/* .MaxVertexAttribs = */ 64,
/* .MaxVertexUniformComponents = */ 16384,
/* .MaxVaryingFloats = */ 128,
/* .MaxVertexTextureImageUnits = */ 32,
/* .MaxCombinedTextureImageUnits = */ 80,
/* .MaxTextureImageUnits = */ 32,
/* .MaxFragmentUniformComponents = */ 16384,
/* .MaxDrawBuffers = */ 8,
/* .MaxVertexUniformVectors = */ 4096,
/* .MaxVaryingVectors = */ 32,
/* .MaxFragmentUniformVectors = */ 4096,
/* .MaxVertexOutputVectors = */ 32,
/* .MaxFragmentInputVectors = */ 31,
/* .MinProgramTexelOffset = */ -8,
/* .MaxProgramTexelOffset = */ 7,
/* .MaxClipDistances = */ 8,
/* .MaxComputeWorkGroupCountX = */ 65535,
/* .MaxComputeWorkGroupCountY = */ 65535,
/* .MaxComputeWorkGroupCountZ = */ 65535,
/* .MaxComputeWorkGroupSizeX = */ 1024,
/* .MaxComputeWorkGroupSizeY = */ 1024,
/* .MaxComputeWorkGroupSizeZ = */ 64,
/* .MaxComputeUniformComponents = */ 1024,
/* .MaxComputeTextureImageUnits = */ 32,
/* .MaxComputeImageUniforms = */ 16,
/* .MaxComputeAtomicCounters = */ 4096,
/* .MaxComputeAtomicCounterBuffers = */ 8,
/* .MaxVaryingComponents = */ 128,
/* .MaxVertexOutputComponents = */ 128,
/* .MaxGeometryInputComponents = */ 128,
/* .MaxGeometryOutputComponents = */ 128,
/* .MaxFragmentInputComponents = */ 128,
/* .MaxImageUnits = */ 192,
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
/* .MaxCombinedShaderOutputResources = */ 144,
/* .MaxImageSamples = */ 32,
/* .MaxVertexImageUniforms = */ 16,
/* .MaxTessControlImageUniforms = */ 16,
/* .MaxTessEvaluationImageUniforms = */ 16,
/* .MaxGeometryImageUniforms = */ 16,
/* .MaxFragmentImageUniforms = */ 16,
/* .MaxCombinedImageUniforms = */ 80,
/* .MaxGeometryTextureImageUnits = */ 16,
/* .MaxGeometryOutputVertices = */ 256,
/* .MaxGeometryTotalOutputComponents = */ 1024,
/* .MaxGeometryUniformComponents = */ 1024,
/* .MaxGeometryVaryingComponents = */ 64,
/* .MaxTessControlInputComponents = */ 128,
/* .MaxTessControlOutputComponents = */ 128,
/* .MaxTessControlTextureImageUnits = */ 16,
/* .MaxTessControlUniformComponents = */ 1024,
/* .MaxTessControlTotalOutputComponents = */ 4096,
/* .MaxTessEvaluationInputComponents = */ 128,
/* .MaxTessEvaluationOutputComponents = */ 128,
/* .MaxTessEvaluationTextureImageUnits = */ 16,
/* .MaxTessEvaluationUniformComponents = */ 1024,
/* .MaxTessPatchComponents = */ 120,
/* .MaxPatchVertices = */ 32,
/* .MaxTessGenLevel = */ 64,
/* .MaxViewports = */ 16,
/* .MaxVertexAtomicCounters = */ 4096,
/* .MaxTessControlAtomicCounters = */ 4096,
/* .MaxTessEvaluationAtomicCounters = */ 4096,
/* .MaxGeometryAtomicCounters = */ 4096,
/* .MaxFragmentAtomicCounters = */ 4096,
/* .MaxCombinedAtomicCounters = */ 4096,
/* .MaxAtomicCounterBindings = */ 8,
/* .MaxVertexAtomicCounterBuffers = */ 8,
/* .MaxTessControlAtomicCounterBuffers = */ 8,
/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
/* .MaxGeometryAtomicCounterBuffers = */ 8,
/* .MaxFragmentAtomicCounterBuffers = */ 8,
/* .MaxCombinedAtomicCounterBuffers = */ 8,
/* .MaxAtomicCounterBufferSize = */ 16384,
/* .MaxTransformFeedbackBuffers = */ 4,
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
/* .MaxCullDistances = */ 8,
/* .MaxCombinedClipAndCullDistances = */ 8,
/* .MaxSamples = */ 32,
/* .maxMeshOutputVerticesNV = */ 256,
/* .maxMeshOutputPrimitivesNV = */ 512,
/* .maxMeshWorkGroupSizeX_NV = */ 32,
/* .maxMeshWorkGroupSizeY_NV = */ 1,
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
/* .maxTaskWorkGroupSizeX_NV = */ 32,
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}
};
namespace graphics {
namespace vulkan {
static const TBuiltInResource defaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
/* .MaxTextureUnits = */ 32,
/* .MaxTextureCoords = */ 32,
/* .MaxVertexAttribs = */ 64,
/* .MaxVertexUniformComponents = */ 16384,
/* .MaxVaryingFloats = */ 128,
/* .MaxVertexTextureImageUnits = */ 32,
/* .MaxCombinedTextureImageUnits = */ 80,
/* .MaxTextureImageUnits = */ 32,
/* .MaxFragmentUniformComponents = */ 16384,
/* .MaxDrawBuffers = */ 8,
/* .MaxVertexUniformVectors = */ 4096,
/* .MaxVaryingVectors = */ 32,
/* .MaxFragmentUniformVectors = */ 4096,
/* .MaxVertexOutputVectors = */ 32,
/* .MaxFragmentInputVectors = */ 31,
/* .MinProgramTexelOffset = */ -8,
/* .MaxProgramTexelOffset = */ 7,
/* .MaxClipDistances = */ 8,
/* .MaxComputeWorkGroupCountX = */ 65535,
/* .MaxComputeWorkGroupCountY = */ 65535,
/* .MaxComputeWorkGroupCountZ = */ 65535,
/* .MaxComputeWorkGroupSizeX = */ 1024,
/* .MaxComputeWorkGroupSizeY = */ 1024,
/* .MaxComputeWorkGroupSizeZ = */ 64,
/* .MaxComputeUniformComponents = */ 1024,
/* .MaxComputeTextureImageUnits = */ 32,
/* .MaxComputeImageUniforms = */ 16,
/* .MaxComputeAtomicCounters = */ 4096,
/* .MaxComputeAtomicCounterBuffers = */ 8,
/* .MaxVaryingComponents = */ 128,
/* .MaxVertexOutputComponents = */ 128,
/* .MaxGeometryInputComponents = */ 128,
/* .MaxGeometryOutputComponents = */ 128,
/* .MaxFragmentInputComponents = */ 128,
/* .MaxImageUnits = */ 192,
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
/* .MaxCombinedShaderOutputResources = */ 144,
/* .MaxImageSamples = */ 32,
/* .MaxVertexImageUniforms = */ 16,
/* .MaxTessControlImageUniforms = */ 16,
/* .MaxTessEvaluationImageUniforms = */ 16,
/* .MaxGeometryImageUniforms = */ 16,
/* .MaxFragmentImageUniforms = */ 16,
/* .MaxCombinedImageUniforms = */ 80,
/* .MaxGeometryTextureImageUnits = */ 16,
/* .MaxGeometryOutputVertices = */ 256,
/* .MaxGeometryTotalOutputComponents = */ 1024,
/* .MaxGeometryUniformComponents = */ 1024,
/* .MaxGeometryVaryingComponents = */ 64,
/* .MaxTessControlInputComponents = */ 128,
/* .MaxTessControlOutputComponents = */ 128,
/* .MaxTessControlTextureImageUnits = */ 16,
/* .MaxTessControlUniformComponents = */ 1024,
/* .MaxTessControlTotalOutputComponents = */ 4096,
/* .MaxTessEvaluationInputComponents = */ 128,
/* .MaxTessEvaluationOutputComponents = */ 128,
/* .MaxTessEvaluationTextureImageUnits = */ 16,
/* .MaxTessEvaluationUniformComponents = */ 1024,
/* .MaxTessPatchComponents = */ 120,
/* .MaxPatchVertices = */ 32,
/* .MaxTessGenLevel = */ 64,
/* .MaxViewports = */ 16,
/* .MaxVertexAtomicCounters = */ 4096,
/* .MaxTessControlAtomicCounters = */ 4096,
/* .MaxTessEvaluationAtomicCounters = */ 4096,
/* .MaxGeometryAtomicCounters = */ 4096,
/* .MaxFragmentAtomicCounters = */ 4096,
/* .MaxCombinedAtomicCounters = */ 4096,
/* .MaxAtomicCounterBindings = */ 8,
/* .MaxVertexAtomicCounterBuffers = */ 8,
/* .MaxTessControlAtomicCounterBuffers = */ 8,
/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
/* .MaxGeometryAtomicCounterBuffers = */ 8,
/* .MaxFragmentAtomicCounterBuffers = */ 8,
/* .MaxCombinedAtomicCounterBuffers = */ 8,
/* .MaxAtomicCounterBufferSize = */ 16384,
/* .MaxTransformFeedbackBuffers = */ 4,
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
/* .MaxCullDistances = */ 8,
/* .MaxCombinedClipAndCullDistances = */ 8,
/* .MaxSamples = */ 32,
/* .maxMeshOutputVerticesNV = */ 256,
/* .maxMeshOutputPrimitivesNV = */ 512,
/* .maxMeshWorkGroupSizeX_NV = */ 32,
/* .maxMeshWorkGroupSizeY_NV = */ 1,
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
/* .maxTaskWorkGroupSizeX_NV = */ 32,
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}
};
static const uint32_t STREAMBUFFER_SIZE = 1024;
static const uint32_t STREAMBUFFER_SIZE = 1024;
static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
switch (type) {
case SHADERSTAGE_VERTEX:
return VK_SHADER_STAGE_VERTEX_BIT;
case SHADERSTAGE_PIXEL:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case SHADERSTAGE_COMPUTE:
return VK_SHADER_STAGE_COMPUTE_BIT;
}
throw love::Exception("invalid type");
}
static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
switch (type) {
case SHADERSTAGE_VERTEX:
return VK_SHADER_STAGE_VERTEX_BIT;
case SHADERSTAGE_PIXEL:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case SHADERSTAGE_COMPUTE:
return VK_SHADER_STAGE_COMPUTE_BIT;
}
throw love::Exception("invalid type");
}
static EShLanguage getGlslShaderType(ShaderStageType stage) {
switch (stage) {
case SHADERSTAGE_VERTEX:
return EShLangVertex;
case SHADERSTAGE_PIXEL:
return EShLangFragment;
case SHADERSTAGE_COMPUTE:
return EShLangCompute;
default:
throw love::Exception("unkonwn shader stage type");
}
}
Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[])
: graphics::Shader(stages) {
loadVolatile();
}
bool Shader::loadVolatile() {
calculateUniformBufferSizeAligned();
compileShaders();
createDescriptorSetLayout();
createPipelineLayout();
createStreamBuffers();
currentImage = 0;
count = 0;
return true;
}
void Shader::unloadVolatile() {
if (shaderModules.size() == 0) {
return;
}
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout](){
for (const auto shaderModule : shaderModules) {
vkDestroyShaderModule(device, shaderModule, nullptr);
}
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
});
for (const auto streamBuffer : streamBuffers) {
delete streamBuffer;
}
shaderModules.clear();
shaderStages.clear();
streamBuffers.clear();
}
const std::vector<VkPipelineShaderStageCreateInfo>& Shader::getShaderStages() const {
return shaderStages;
}
const VkPipelineLayout Shader::getGraphicsPipelineLayout() const {
return pipelineLayout;
}
static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) {
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
Texture* vkTexture = (Texture*)texture;
imageInfo.imageView = vkTexture->getImageView();
imageInfo.sampler = vkTexture->getSampler();
return imageInfo;
}
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
if (currentImage != imageIndex) {
currentImage = imageIndex;
count = 0;
streamBuffers[currentImage]->nextFrame();
}
else {
if (count >= STREAMBUFFER_SIZE) {
throw love::Exception("uniform stream buffer: out of memory (fixme: resize)");
}
}
auto mapInfo = streamBuffers[currentImage]->map(uniformBufferSizeAligned);
memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned);
streamBuffers[currentImage]->unmap(uniformBufferSizeAligned);
streamBuffers[currentImage]->markUsed(uniformBufferSizeAligned);
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle();
bufferInfo.offset = count * uniformBufferSizeAligned;
bufferInfo.range = sizeof(BuiltinUniformData);
auto mainTexImageInfo = createDescriptorImageInfo(mainTex);
auto ytextureImageInfo = createDescriptorImageInfo(ytexture);
auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture);
auto crtextureImageInfo = createDescriptorImageInfo(crtexture);
std::array<VkWriteDescriptorSet, 5> descriptorWrite{};
descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[0].dstSet = 0;
descriptorWrite[0].dstBinding = 0;
descriptorWrite[0].dstArrayElement = 0;
descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite[0].descriptorCount = 1;
descriptorWrite[0].pBufferInfo = &bufferInfo;
descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[1].dstSet = 0;
descriptorWrite[1].dstBinding = 1;
descriptorWrite[1].dstArrayElement = 0;
descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[1].descriptorCount = 1;
descriptorWrite[1].pImageInfo = &mainTexImageInfo;
descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[2].dstSet = 0;
descriptorWrite[2].dstBinding = 2;
descriptorWrite[2].dstArrayElement = 0;
descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[2].descriptorCount = 1;
descriptorWrite[2].pImageInfo = &ytextureImageInfo;
descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[3].dstSet = 0;
descriptorWrite[3].dstBinding = 3;
descriptorWrite[3].dstArrayElement = 0;
descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[3].descriptorCount = 1;
descriptorWrite[3].pImageInfo = &cbtextureImageInfo;
descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[4].dstSet = 0;
descriptorWrite[4].dstBinding = 4;
descriptorWrite[4].dstArrayElement = 0;
descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[4].descriptorCount = 1;
descriptorWrite[4].pImageInfo = &crtextureImageInfo;
vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data());
count++;
}
Shader::~Shader() {
unloadVolatile();
}
void Shader::attach() {
if (Shader::current != this) {
Graphics::flushBatchedDrawsGlobal();
Shader::current = this;
Vulkan::shaderSwitch();
}
}
int Shader::getVertexAttributeIndex(const std::string& name) {
return vertexAttributeIndices.at(name);
}
void Shader::calculateUniformBufferSizeAligned() {
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx;
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
uniformBufferSizeAligned =
static_cast<VkDeviceSize>(
std::ceil(
static_cast<float>(sizeof(BuiltinUniformData)) / static_cast<float>(minAlignment)
)
)
* minAlignment;
}
void Shader::compileShaders() {
using namespace glslang;
using namespace spirv_cross;
TProgram* program = new TProgram();
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx;
device = vgfx->getDevice();
mainTex = vgfx->getDefaultTexture();
ytexture = vgfx->getDefaultTexture();
crtexture = vgfx->getDefaultTexture();
cbtexture = vgfx->getDefaultTexture();
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
if (!stages[i])
continue;
auto stage = (ShaderStageType)i;
auto glslangShaderStage = getGlslShaderType(stage);
auto tshader = new TShader(glslangShaderStage);
tshader->setEnvInput(EShSourceGlsl, glslangShaderStage, EShClientVulkan, 450);
tshader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2);
tshader->setEnvTarget(EshTargetSpv, EShTargetSpv_1_5);
tshader->setAutoMapLocations(true);
tshader->setAutoMapBindings(true);
tshader->setEnvInputVulkanRulesRelaxed();
tshader->setGlobalUniformBinding(0);
tshader->setGlobalUniformSet(0);
auto& glsl = stages[i]->getSource();
const char* csrc = glsl.c_str();
const int sourceLength = static_cast<int>(glsl.length());
tshader->setStringsWithLengths(&csrc, &sourceLength, 1);
int defaultVersio = 450;
EProfile defaultProfile = ECoreProfile;
bool forceDefault = false;
bool forwardCompat = true;
if (!tshader->parse(&defaultTBuiltInResource, defaultVersio, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings)) {
const char* msg1 = tshader->getInfoLog();
const char* msg2 = tshader->getInfoDebugLog();
throw love::Exception("error while parsing shader");
}
program->addShader(tshader);
}
if (!program->link(EShMsgDefault)) {
throw love::Exception("link failed! %s\n", program->getInfoLog());
}
if (!program->mapIO()) {
throw love::Exception("mapIO failed");
}
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
auto glslangStage = getGlslShaderType((ShaderStageType)i);
auto intermediate = program->getIntermediate(glslangStage);
if (intermediate == nullptr) {
continue;
}
spv::SpvBuildLogger logger;
glslang::SpvOptions opt;
opt.validate = true;
std::vector<uint32_t> spirv;
GlslangToSpv(*intermediate, spirv, &logger, &opt);
std::string msgs = logger.getAllMessages();
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = spirv.size() * sizeof(uint32_t);
createInfo.pCode = spirv.data();
Graphics* vkGfx = (Graphics*)gfx;
auto device = vkGfx->getDevice();
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw love::Exception("failed to create shader module");
}
shaderModules.push_back(shaderModule);
VkPipelineShaderStageCreateInfo shaderStageInfo{};
shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageInfo.stage = getStageBit((ShaderStageType)i);
shaderStageInfo.module = shaderModule;
shaderStageInfo.pName = "main";
shaderStages.push_back(shaderStageInfo);
}
}
// fixme: should generate this dynamically.
void Shader::createDescriptorSetLayout() {
auto vgfx = (Graphics*)gfx;
vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer();
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoYBinding{};
videoYBinding.binding = 2;
videoYBinding.descriptorCount = 1;
videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoYBinding.pImmutableSamplers = nullptr;
videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCBBinding{};
videoCBBinding.binding = 3;
videoCBBinding.descriptorCount = 1;
videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCBBinding.pImmutableSamplers = nullptr;
videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCRinding{};
videoCRinding.binding = 4;
videoCRinding.descriptorCount = 1;
videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCRinding.pImmutableSamplers = nullptr;
videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 5> bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding };
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
throw love::Exception("failed to create descriptor set layout");
}
}
void Shader::createPipelineLayout() {
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw love::Exception("failed to create pipeline layout");
}
}
void Shader::createStreamBuffers() {
auto vgfx = (Graphics*)gfx;
const auto numImagesInFlight = vgfx->getNumImagesInFlight();
streamBuffers.resize(numImagesInFlight);
for (uint32_t i = 0; i < numImagesInFlight; i++) {
streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * uniformBufferSizeAligned);
}
}
void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) {
this->ytexture = ytexture;
this->cbtexture = cbtexture;
this->crtexture = crtexture;
}
void Shader::setUniformData(BuiltinUniformData& data) {
uniformData = data;
}
void Shader::setMainTex(graphics::Texture* texture) {
mainTex = texture;
}
}
static EShLanguage getGlslShaderType(ShaderStageType stage) {
switch (stage) {
case SHADERSTAGE_VERTEX:
return EShLangVertex;
case SHADERSTAGE_PIXEL:
return EShLangFragment;
case SHADERSTAGE_COMPUTE:
return EShLangCompute;
default:
throw love::Exception("unkonwn shader stage type");
}
}
Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[])
: graphics::Shader(stages) {
loadVolatile();
}
bool Shader::loadVolatile() {
calculateUniformBufferSizeAligned();
compileShaders();
createDescriptorSetLayout();
createPipelineLayout();
createStreamBuffers();
currentImage = 0;
count = 0;
return true;
}
void Shader::unloadVolatile() {
if (shaderModules.size() == 0) {
return;
}
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout](){
for (const auto shaderModule : shaderModules) {
vkDestroyShaderModule(device, shaderModule, nullptr);
}
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
});
for (const auto streamBuffer : streamBuffers) {
delete streamBuffer;
}
shaderModules.clear();
shaderStages.clear();
streamBuffers.clear();
}
const std::vector<VkPipelineShaderStageCreateInfo>& Shader::getShaderStages() const {
return shaderStages;
}
const VkPipelineLayout Shader::getGraphicsPipelineLayout() const {
return pipelineLayout;
}
static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) {
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
Texture* vkTexture = (Texture*)texture;
imageInfo.imageView = vkTexture->getImageView();
imageInfo.sampler = vkTexture->getSampler();
return imageInfo;
}
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
if (currentImage != imageIndex) {
currentImage = imageIndex;
count = 0;
streamBuffers[currentImage]->nextFrame();
}
else {
if (count >= STREAMBUFFER_SIZE) {
throw love::Exception("uniform stream buffer: out of memory (fixme: resize)");
}
}
auto mapInfo = streamBuffers[currentImage]->map(uniformBufferSizeAligned);
memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned);
streamBuffers[currentImage]->unmap(uniformBufferSizeAligned);
streamBuffers[currentImage]->markUsed(uniformBufferSizeAligned);
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle();
bufferInfo.offset = count * uniformBufferSizeAligned;
bufferInfo.range = sizeof(BuiltinUniformData);
auto mainTexImageInfo = createDescriptorImageInfo(mainTex);
auto ytextureImageInfo = createDescriptorImageInfo(ytexture);
auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture);
auto crtextureImageInfo = createDescriptorImageInfo(crtexture);
std::array<VkWriteDescriptorSet, 5> descriptorWrite{};
descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[0].dstSet = 0;
descriptorWrite[0].dstBinding = 0;
descriptorWrite[0].dstArrayElement = 0;
descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite[0].descriptorCount = 1;
descriptorWrite[0].pBufferInfo = &bufferInfo;
descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[1].dstSet = 0;
descriptorWrite[1].dstBinding = 1;
descriptorWrite[1].dstArrayElement = 0;
descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[1].descriptorCount = 1;
descriptorWrite[1].pImageInfo = &mainTexImageInfo;
descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[2].dstSet = 0;
descriptorWrite[2].dstBinding = 2;
descriptorWrite[2].dstArrayElement = 0;
descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[2].descriptorCount = 1;
descriptorWrite[2].pImageInfo = &ytextureImageInfo;
descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[3].dstSet = 0;
descriptorWrite[3].dstBinding = 3;
descriptorWrite[3].dstArrayElement = 0;
descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[3].descriptorCount = 1;
descriptorWrite[3].pImageInfo = &cbtextureImageInfo;
descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite[4].dstSet = 0;
descriptorWrite[4].dstBinding = 4;
descriptorWrite[4].dstArrayElement = 0;
descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite[4].descriptorCount = 1;
descriptorWrite[4].pImageInfo = &crtextureImageInfo;
vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data());
count++;
}
Shader::~Shader() {
unloadVolatile();
}
void Shader::attach() {
if (Shader::current != this) {
Graphics::flushBatchedDrawsGlobal();
Shader::current = this;
Vulkan::shaderSwitch();
}
}
int Shader::getVertexAttributeIndex(const std::string& name) {
return vertexAttributeIndices.at(name);
}
void Shader::calculateUniformBufferSizeAligned() {
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx;
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
uniformBufferSizeAligned =
static_cast<VkDeviceSize>(
std::ceil(
static_cast<float>(sizeof(BuiltinUniformData)) / static_cast<float>(minAlignment)
)
)
* minAlignment;
}
void Shader::compileShaders() {
using namespace glslang;
using namespace spirv_cross;
TProgram* program = new TProgram();
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx;
device = vgfx->getDevice();
mainTex = vgfx->getDefaultTexture();
ytexture = vgfx->getDefaultTexture();
crtexture = vgfx->getDefaultTexture();
cbtexture = vgfx->getDefaultTexture();
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
if (!stages[i])
continue;
auto stage = (ShaderStageType)i;
auto glslangShaderStage = getGlslShaderType(stage);
auto tshader = new TShader(glslangShaderStage);
tshader->setEnvInput(EShSourceGlsl, glslangShaderStage, EShClientVulkan, 450);
tshader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2);
tshader->setEnvTarget(EshTargetSpv, EShTargetSpv_1_5);
tshader->setAutoMapLocations(true);
tshader->setAutoMapBindings(true);
tshader->setEnvInputVulkanRulesRelaxed();
tshader->setGlobalUniformBinding(0);
tshader->setGlobalUniformSet(0);
auto& glsl = stages[i]->getSource();
const char* csrc = glsl.c_str();
const int sourceLength = static_cast<int>(glsl.length());
tshader->setStringsWithLengths(&csrc, &sourceLength, 1);
int defaultVersio = 450;
EProfile defaultProfile = ECoreProfile;
bool forceDefault = false;
bool forwardCompat = true;
if (!tshader->parse(&defaultTBuiltInResource, defaultVersio, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings)) {
const char* msg1 = tshader->getInfoLog();
const char* msg2 = tshader->getInfoDebugLog();
throw love::Exception("error while parsing shader");
}
program->addShader(tshader);
}
if (!program->link(EShMsgDefault)) {
throw love::Exception("link failed! %s\n", program->getInfoLog());
}
if (!program->mapIO()) {
throw love::Exception("mapIO failed");
}
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
auto glslangStage = getGlslShaderType((ShaderStageType)i);
auto intermediate = program->getIntermediate(glslangStage);
if (intermediate == nullptr) {
continue;
}
spv::SpvBuildLogger logger;
glslang::SpvOptions opt;
opt.validate = true;
std::vector<uint32_t> spirv;
GlslangToSpv(*intermediate, spirv, &logger, &opt);
std::string msgs = logger.getAllMessages();
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = spirv.size() * sizeof(uint32_t);
createInfo.pCode = spirv.data();
Graphics* vkGfx = (Graphics*)gfx;
auto device = vkGfx->getDevice();
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw love::Exception("failed to create shader module");
}
shaderModules.push_back(shaderModule);
VkPipelineShaderStageCreateInfo shaderStageInfo{};
shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageInfo.stage = getStageBit((ShaderStageType)i);
shaderStageInfo.module = shaderModule;
shaderStageInfo.pName = "main";
shaderStages.push_back(shaderStageInfo);
}
}
// fixme: should generate this dynamically.
void Shader::createDescriptorSetLayout() {
auto vgfx = (Graphics*)gfx;
vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer();
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoYBinding{};
videoYBinding.binding = 2;
videoYBinding.descriptorCount = 1;
videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoYBinding.pImmutableSamplers = nullptr;
videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCBBinding{};
videoCBBinding.binding = 3;
videoCBBinding.descriptorCount = 1;
videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCBBinding.pImmutableSamplers = nullptr;
videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutBinding videoCRinding{};
videoCRinding.binding = 4;
videoCRinding.descriptorCount = 1;
videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
videoCRinding.pImmutableSamplers = nullptr;
videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 5> bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding };
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
throw love::Exception("failed to create descriptor set layout");
}
}
void Shader::createPipelineLayout() {
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw love::Exception("failed to create pipeline layout");
}
}
void Shader::createStreamBuffers() {
auto vgfx = (Graphics*)gfx;
const auto numImagesInFlight = vgfx->getNumImagesInFlight();
streamBuffers.resize(numImagesInFlight);
for (uint32_t i = 0; i < numImagesInFlight; i++) {
streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * uniformBufferSizeAligned);
}
}
void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) {
this->ytexture = ytexture;
this->cbtexture = cbtexture;
this->crtexture = crtexture;
}
void Shader::setUniformData(BuiltinUniformData& data) {
uniformData = data;
}
void Shader::setMainTex(graphics::Texture* texture) {
mainTex = texture;
}
} // vulkan
} // graphics
} // love
+62 -62
View File
@@ -11,91 +11,91 @@
namespace love {
namespace graphics {
namespace vulkan {
class Shader final : public graphics::Shader, public Volatile {
public:
Shader(StrongRef<love::graphics::ShaderStage> stages[]);
virtual ~Shader();
namespace graphics {
namespace vulkan {
class Shader final : public graphics::Shader, public Volatile {
public:
Shader(StrongRef<love::graphics::ShaderStage> stages[]);
virtual ~Shader();
bool loadVolatile() override;
void unloadVolatile() override;
bool loadVolatile() override;
void unloadVolatile() override;
const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const;
const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const;
const VkPipelineLayout getGraphicsPipelineLayout() const;
const VkPipelineLayout getGraphicsPipelineLayout() const;
void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
void attach() override;
void attach() override;
ptrdiff_t getHandle() const { return 0; }
ptrdiff_t getHandle() const { return 0; }
std::string getWarnings() const override { return ""; }
std::string getWarnings() const override { return ""; }
int getVertexAttributeIndex(const std::string& name) override;
int getVertexAttributeIndex(const std::string& name) override;
const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; }
const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; }
const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; }
const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; }
void updateUniform(const UniformInfo* info, int count) override {}
void updateUniform(const UniformInfo* info, int count) override {}
void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override {}
void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {}
void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override {}
void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {}
bool hasUniform(const std::string& name) const override { return false; }
bool hasUniform(const std::string& name) const override { return false; }
void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override;
void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override;
// fixme: use normal methods for this in the future.
void setUniformData(BuiltinUniformData& data);
void setMainTex(graphics::Texture* texture);
// fixme: use normal methods for this in the future.
void setUniformData(BuiltinUniformData& data);
void setMainTex(graphics::Texture* texture);
private:
void calculateUniformBufferSizeAligned();
void compileShaders();
void createDescriptorSetLayout();
void createPipelineLayout();
void createStreamBuffers();
private:
void calculateUniformBufferSizeAligned();
void compileShaders();
void createDescriptorSetLayout();
void createPipelineLayout();
void createStreamBuffers();
VkDeviceSize uniformBufferSizeAligned;
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
VkDeviceSize uniformBufferSizeAligned;
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
std::vector<StreamBuffer*> streamBuffers;
std::vector<StreamBuffer*> streamBuffers;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> shaderModules;
Graphics* gfx;
VkDevice device;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> shaderModules;
Graphics* gfx;
VkDevice device;
std::map<std::string, int> vertexAttributeIndices = {
{ "VertexPosition", 0 },
{ "VertexTexCoord", 1 },
{ "VertexColor", 2 }
};
std::map<std::string, int> vertexAttributeIndices = {
{ "VertexPosition", 0 },
{ "VertexTexCoord", 1 },
{ "VertexColor", 2 }
};
std::map<std::string, int> uniformBindings = {
{ "love_UniformsPerDraw", 0 },
{ "love_VideoYChannel", 1 },
{ "love_VideoCbChannel", 2 },
{ "love_VideoCrChannel", 3 },
{ "MainTex", 4 }
};
std::map<std::string, int> uniformBindings = {
{ "love_UniformsPerDraw", 0 },
{ "love_VideoYChannel", 1 },
{ "love_VideoCbChannel", 2 },
{ "love_VideoCrChannel", 3 },
{ "MainTex", 4 }
};
BuiltinUniformData uniformData;
graphics::Texture* mainTex;
graphics::Texture* ytexture;
graphics::Texture* cbtexture;
graphics::Texture* crtexture;
BuiltinUniformData uniformData;
graphics::Texture* mainTex;
graphics::Texture* ytexture;
graphics::Texture* cbtexture;
graphics::Texture* crtexture;
uint32_t currentImage;
uint32_t count;
};
}
}
uint32_t currentImage;
uint32_t count;
};
}
}
}
#endif
+8 -8
View File
@@ -12,12 +12,12 @@
namespace love {
namespace graphics {
namespace vulkan {
ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey)
: love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) {
// the compilation is done in Shader.
}
}
}
namespace graphics {
namespace vulkan {
ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey)
: love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) {
// the compilation is done in Shader.
}
} // love
} // graphics
} // vulkan
+10 -10
View File
@@ -6,18 +6,18 @@
#include <vulkan/vulkan.h>
namespace love {
namespace graphics {
namespace vulkan {
class ShaderStage final : public graphics::ShaderStage {
public:
ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey);
namespace graphics {
namespace vulkan {
class ShaderStage final : public graphics::ShaderStage {
public:
ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey);
ptrdiff_t getHandle() const {
return 0;
}
};
}
ptrdiff_t getHandle() const {
return 0;
}
};
}
}
}
#endif
+70 -70
View File
@@ -4,75 +4,75 @@
namespace love {
namespace graphics {
namespace vulkan {
static VkBufferUsageFlags getUsageFlags(BufferUsage mode) {
switch (mode) {
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
StreamBuffer::StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size)
: love::graphics::StreamBuffer(mode, size), gfx(gfx) {
loadVolatile();
}
bool StreamBuffer::loadVolatile() {
Graphics* vgfx = (Graphics*)gfx;
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = getUsageFlags(mode);
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; // always mapped
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
usedGPUMemory = 0;
return true;
}
void StreamBuffer::unloadVolatile() {
if (buffer == VK_NULL_HANDLE)
return;
auto vgfx = (Graphics*)gfx;
vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
StreamBuffer::~StreamBuffer() {
unloadVolatile();
}
love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) {
(void)minsize;
return love::graphics::StreamBuffer::MapInfo((uint8*) allocInfo.pMappedData + usedGPUMemory, getSize());
}
size_t StreamBuffer::unmap(size_t usedSize) {
return usedGPUMemory;
}
void StreamBuffer::markUsed(size_t usedSize) {
usedGPUMemory += usedSize;
}
void StreamBuffer::nextFrame() {
usedGPUMemory = 0;
}
}
namespace graphics {
namespace vulkan {
static VkBufferUsageFlags getUsageFlags(BufferUsage mode) {
switch (mode) {
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
StreamBuffer::StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size)
: love::graphics::StreamBuffer(mode, size), gfx(gfx) {
loadVolatile();
}
bool StreamBuffer::loadVolatile() {
Graphics* vgfx = (Graphics*)gfx;
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = getUsageFlags(mode);
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; // always mapped
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
usedGPUMemory = 0;
return true;
}
void StreamBuffer::unloadVolatile() {
if (buffer == VK_NULL_HANDLE)
return;
auto vgfx = (Graphics*)gfx;
vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
StreamBuffer::~StreamBuffer() {
unloadVolatile();
}
love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) {
(void)minsize;
return love::graphics::StreamBuffer::MapInfo((uint8*) allocInfo.pMappedData + usedGPUMemory, getSize());
}
size_t StreamBuffer::unmap(size_t usedSize) {
return usedGPUMemory;
}
void StreamBuffer::markUsed(size_t usedSize) {
usedGPUMemory += usedSize;
}
void StreamBuffer::nextFrame() {
usedGPUMemory = 0;
}
} // vulkan
} // graphics
} // love
+27 -27
View File
@@ -9,38 +9,38 @@
#include "vk_mem_alloc.h"
namespace love {
namespace graphics {
namespace vulkan {
class StreamBuffer : public love::graphics::StreamBuffer, public graphics::Volatile {
public:
StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size);
virtual ~StreamBuffer();
namespace graphics {
namespace vulkan {
class StreamBuffer : public love::graphics::StreamBuffer, public graphics::Volatile {
public:
StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size);
virtual ~StreamBuffer();
virtual bool loadVolatile() override;
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
virtual void unloadVolatile() override;
MapInfo map(size_t minsize) override;
size_t unmap(size_t usedSize) override;
void markUsed(size_t usedSize) override;
MapInfo map(size_t minsize) override;
size_t unmap(size_t usedSize) override;
void markUsed(size_t usedSize) override;
void nextFrame() override;
void nextFrame() override;
ptrdiff_t getHandle() const override {
return (ptrdiff_t) buffer;
}
private:
graphics::Graphics* gfx;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
VkBuffer buffer = VK_NULL_HANDLE;
size_t usedGPUMemory;
};
}
ptrdiff_t getHandle() const override {
return (ptrdiff_t) buffer;
}
}
private:
graphics::Graphics* gfx;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
VkBuffer buffer = VK_NULL_HANDLE;
size_t usedGPUMemory;
};
} // vulkan
} // graphics
} // love
#endif
+258 -258
View File
@@ -8,272 +8,272 @@
#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), gfx(gfx), data(data) {
loadVolatile();
}
namespace graphics {
namespace vulkan {
Texture::Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data)
: love::graphics::Texture(gfx, settings, data), gfx(gfx), data(data) {
loadVolatile();
}
bool Texture::loadVolatile() {
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
bool Texture::loadVolatile() {
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
auto vulkanFormat = Vulkan::getTextureFormat(format);
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<uint32_t>(width);
imageInfo.extent.height = static_cast<uint32_t>(height);
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = vulkanFormat.internalFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<uint32_t>(width);
imageInfo.extent.height = static_cast<uint32_t>(height);
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = vulkanFormat.internalFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
throw love::Exception("failed to create image");
}
// fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL.
transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
throw love::Exception("failed to create image");
}
// fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL.
transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
if (data) {
auto sliceData = data->get(0, 0);
auto size = sliceData->getSize();
auto dataPtr = sliceData->getData();
Rect rect{};
rect.x = 0;
rect.y = 0;
rect.w = sliceData->getWidth();
rect.h = sliceData->getHeight();
if (data) {
auto sliceData = data->get(0, 0);
auto size = sliceData->getSize();
auto dataPtr = sliceData->getData();
Rect rect{};
rect.x = 0;
rect.y = 0;
rect.w = sliceData->getWidth();
rect.h = sliceData->getHeight();
uploadByteData(format, dataPtr, size, 0, 0, rect);
} else {
if (isRenderTarget()) {
clear(false);
}
else {
clear(true);
}
}
createTextureImageView();
createTextureSampler();
return true;
}
void Texture::unloadVolatile() {
if (textureImage == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([
device = device,
textureSampler = textureSampler,
textureImageView = textureImageView,
allocator = allocator,
textureImage = textureImage,
textureImageAllocation = textureImageAllocation] () {
vkDestroySampler(device, textureSampler, nullptr);
vkDestroyImageView(device, textureImageView, nullptr);
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
});
textureImage = VK_NULL_HANDLE;
}
Texture::~Texture() {
unloadVolatile();
}
void Texture::createTextureImageView() {
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
viewInfo.components.b = vulkanFormat.swizzleB;
viewInfo.components.a = vulkanFormat.swizzleA;
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::clear(bool white) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
auto clearColor = getClearValue(white);
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.layerCount = getMipmapCount();
range.levelCount = 1;
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range);
vgfx->endSingleTimeCommands(commandBuffer);
}
VkClearColorValue Texture::getClearValue(bool white) {
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkClearColorValue clearColor{};
if (white) {
switch (vulkanFormat.internalFormatRepresentation) {
case FORMATREPRESENTATION_FLOAT:
clearColor.float32[0] = 1.0f;
clearColor.float32[1] = 1.0f;
clearColor.float32[2] = 1.0f;
clearColor.float32[3] = 1.0f;
break;
case FORMATREPRESENTATION_SINT:
clearColor.int32[0] = std::numeric_limits<int32_t>::max();
clearColor.int32[1] = std::numeric_limits<int32_t>::max();
clearColor.int32[2] = std::numeric_limits<int32_t>::max();
clearColor.int32[3] = std::numeric_limits<int32_t>::max();
break;
case FORMATREPRESENTATION_UINT:
clearColor.uint32[0] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[1] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[2] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[3] = std::numeric_limits<uint32_t>::max();
break;
}
}
else {
switch (vulkanFormat.internalFormatRepresentation) {
case FORMATREPRESENTATION_FLOAT:
clearColor.float32[0] = 0.0f;
clearColor.float32[1] = 0.0f;
clearColor.float32[2] = 0.0f;
clearColor.float32[3] = 0.0f;
break;
case FORMATREPRESENTATION_SINT:
clearColor.int32[0] = 0;
clearColor.int32[1] = 0;
clearColor.int32[2] = 0;
clearColor.int32[3] = 0;
break;
case FORMATREPRESENTATION_UINT:
clearColor.uint32[0] = 0;
clearColor.uint32[1] = 0;
clearColor.uint32[2] = 0;
clearColor.uint32[3] = 0;
break;
}
}
return clearColor;
}
void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
Vulkan::cmdTransitionImageLayout(commandBuffer, image, oldLayout, newLayout);
vgfx->endSingleTimeCommands(commandBuffer);
}
void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) {
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);
auto command = [buffer = stagingBuffer, image = textureImage, r = r](VkCommandBuffer commandBuffer) {
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 = { r.x, r.y, 0 };
region.imageExtent = {
static_cast<uint32_t>(r.w),
static_cast<uint32_t>(r.h), 1
};
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
};
auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
};
vgfx->queueDatatransfer(command, cleanUp);
}
uploadByteData(format, dataPtr, size, 0, 0, rect);
} else {
if (isRenderTarget()) {
clear(false);
}
else {
clear(true);
}
}
createTextureImageView();
createTextureSampler();
return true;
}
void Texture::unloadVolatile() {
if (textureImage == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([
device = device,
textureSampler = textureSampler,
textureImageView = textureImageView,
allocator = allocator,
textureImage = textureImage,
textureImageAllocation = textureImageAllocation] () {
vkDestroySampler(device, textureSampler, nullptr);
vkDestroyImageView(device, textureImageView, nullptr);
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
});
textureImage = VK_NULL_HANDLE;
}
Texture::~Texture() {
unloadVolatile();
}
void Texture::createTextureImageView() {
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
viewInfo.components.b = vulkanFormat.swizzleB;
viewInfo.components.a = vulkanFormat.swizzleA;
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::clear(bool white) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
auto clearColor = getClearValue(white);
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.layerCount = getMipmapCount();
range.levelCount = 1;
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range);
vgfx->endSingleTimeCommands(commandBuffer);
}
VkClearColorValue Texture::getClearValue(bool white) {
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkClearColorValue clearColor{};
if (white) {
switch (vulkanFormat.internalFormatRepresentation) {
case FORMATREPRESENTATION_FLOAT:
clearColor.float32[0] = 1.0f;
clearColor.float32[1] = 1.0f;
clearColor.float32[2] = 1.0f;
clearColor.float32[3] = 1.0f;
break;
case FORMATREPRESENTATION_SINT:
clearColor.int32[0] = std::numeric_limits<int32_t>::max();
clearColor.int32[1] = std::numeric_limits<int32_t>::max();
clearColor.int32[2] = std::numeric_limits<int32_t>::max();
clearColor.int32[3] = std::numeric_limits<int32_t>::max();
break;
case FORMATREPRESENTATION_UINT:
clearColor.uint32[0] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[1] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[2] = std::numeric_limits<uint32_t>::max();
clearColor.uint32[3] = std::numeric_limits<uint32_t>::max();
break;
}
}
else {
switch (vulkanFormat.internalFormatRepresentation) {
case FORMATREPRESENTATION_FLOAT:
clearColor.float32[0] = 0.0f;
clearColor.float32[1] = 0.0f;
clearColor.float32[2] = 0.0f;
clearColor.float32[3] = 0.0f;
break;
case FORMATREPRESENTATION_SINT:
clearColor.int32[0] = 0;
clearColor.int32[1] = 0;
clearColor.int32[2] = 0;
clearColor.int32[3] = 0;
break;
case FORMATREPRESENTATION_UINT:
clearColor.uint32[0] = 0;
clearColor.uint32[1] = 0;
clearColor.uint32[2] = 0;
clearColor.uint32[3] = 0;
break;
}
}
return clearColor;
}
void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
auto commandBuffer = vgfx->beginSingleTimeCommands();
Vulkan::cmdTransitionImageLayout(commandBuffer, image, oldLayout, newLayout);
vgfx->endSingleTimeCommands(commandBuffer);
}
void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) {
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);
auto command = [buffer = stagingBuffer, image = textureImage, r = r](VkCommandBuffer commandBuffer) {
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 = { r.x, r.y, 0 };
region.imageExtent = {
static_cast<uint32_t>(r.w),
static_cast<uint32_t>(r.h), 1
};
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
};
auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
};
vgfx->queueDatatransfer(command, cleanUp);
}
} // vulkan
} // graphics
} // love
+36 -36
View File
@@ -10,50 +10,50 @@
namespace love {
namespace graphics {
namespace vulkan {
class Texture : public graphics::Texture, public Volatile {
public:
Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data);
~Texture();
namespace graphics {
namespace vulkan {
class Texture : public graphics::Texture, public Volatile {
public:
Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data);
~Texture();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { };
void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { };
void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { };
void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { };
ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; };
ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; };
ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; };
ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; };
void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override;
void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override;
void generateMipmapsInternal() override { };
void generateMipmapsInternal() override { };
int getMSAA() const override { return 0; };
ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; }
VkImageView getImageView() const { return textureImageView; }
VkSampler getSampler() const { return textureSampler; }
int getMSAA() const override { return 0; };
ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; }
VkImageView getImageView() const { return textureImageView; }
VkSampler getSampler() const { return textureSampler; }
private:
void transitionImageLayout(VkImage, VkImageLayout oldLayout, VkImageLayout newLayout);
void createTextureImageView();
void createTextureSampler();
void clear(bool white);
private:
void transitionImageLayout(VkImage, VkImageLayout oldLayout, VkImageLayout newLayout);
void createTextureImageView();
void createTextureSampler();
void clear(bool white);
VkClearColorValue getClearValue(bool white);
VkClearColorValue getClearValue(bool white);
graphics::Graphics* gfx;
VkDevice device;
VmaAllocator allocator;
VkImage textureImage = VK_NULL_HANDLE;
VmaAllocation textureImageAllocation;
VkImageView textureImageView;
VkSampler textureSampler;
const Slices* data;
};
}
}
}
graphics::Graphics* gfx;
VkDevice device;
VmaAllocator allocator;
VkImage textureImage = VK_NULL_HANDLE;
VmaAllocation textureImageAllocation;
VkImageView textureImageView;
VkSampler textureSampler;
const Slices* data;
};
} // vulkan
} // graphics
} // love
#endif
File diff suppressed because it is too large Load Diff
+37 -37
View File
@@ -5,47 +5,47 @@
#include "vulkan/vulkan.h"
namespace love {
namespace graphics {
namespace vulkan {
enum InternalFormatRepresentation {
FORMATREPRESENTATION_FLOAT,
FORMATREPRESENTATION_UINT,
FORMATREPRESENTATION_SINT,
FORMATREPRESENTATION_MAX_ENUM
};
namespace graphics {
namespace vulkan {
enum InternalFormatRepresentation {
FORMATREPRESENTATION_FLOAT,
FORMATREPRESENTATION_UINT,
FORMATREPRESENTATION_SINT,
FORMATREPRESENTATION_MAX_ENUM
};
struct TextureFormat {
InternalFormatRepresentation internalFormatRepresentation;
VkFormat internalFormat = VK_FORMAT_UNDEFINED;
struct TextureFormat {
InternalFormatRepresentation internalFormatRepresentation;
VkFormat internalFormat = VK_FORMAT_UNDEFINED;
VkComponentSwizzle swizzleR = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleG = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleB = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleA = VK_COMPONENT_SWIZZLE_IDENTITY;
};
VkComponentSwizzle swizzleR = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleG = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleB = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleA = VK_COMPONENT_SWIZZLE_IDENTITY;
};
class Vulkan {
public:
static void shaderSwitch();
static uint32_t getNumShaderSwitches();
static void resetShaderSwitches();
class Vulkan {
public:
static void shaderSwitch();
static uint32_t getNumShaderSwitches();
static void resetShaderSwitches();
static VkFormat getVulkanVertexFormat(DataFormat format);
static TextureFormat getTextureFormat(PixelFormat);
static std::string getVendorName(uint32_t vendorId);
static std::string getVulkanApiVersion(uint32_t apiVersion);
static VkPrimitiveTopology getPrimitiveTypeTopology(graphics::PrimitiveType);
static VkBlendFactor getBlendFactor(BlendFactor);
static VkBlendOp getBlendOp(BlendOperation);
static VkBool32 getBool(bool);
static VkColorComponentFlags getColorMask(ColorChannelMask);
static VkFrontFace getFrontFace(Winding);
static VkCullModeFlags getCullMode(CullMode);
static VkFormat getVulkanVertexFormat(DataFormat format);
static TextureFormat getTextureFormat(PixelFormat);
static std::string getVendorName(uint32_t vendorId);
static std::string getVulkanApiVersion(uint32_t apiVersion);
static VkPrimitiveTopology getPrimitiveTypeTopology(graphics::PrimitiveType);
static VkBlendFactor getBlendFactor(BlendFactor);
static VkBlendOp getBlendOp(BlendOperation);
static VkBool32 getBool(bool);
static VkColorComponentFlags getColorMask(ColorChannelMask);
static VkFrontFace getFrontFace(Winding);
static VkCullModeFlags getCullMode(CullMode);
static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout);
};
}
}
}
static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout);
};
} // vulkan
} // graphics
} // love
#endif