mirror of
https://github.com/love2d/love.git
synced 2026-08-12 16:40:57 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1594
-1594
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
®ion
|
||||
);
|
||||
|
||||
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,
|
||||
®ion
|
||||
);
|
||||
|
||||
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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user