Merge pull request #1789 from nikeinikei/vulkan

Vulkan Graphics Backend
This commit is contained in:
slime73
2022-09-19 16:44:06 -03:00
committed by GitHub
33 changed files with 31166 additions and 61 deletions
+11 -1
View File
@@ -109,16 +109,21 @@ namespace opengl { extern love::graphics::Graphics *createInstance(); }
#ifdef LOVE_GRAPHICS_METAL
namespace metal { extern love::graphics::Graphics *createInstance(); }
#endif
#ifdef LOVE_GRAPHICS_VULKAN
namespace vulkan { extern love::graphics::Graphics* createInstance(); }
#endif
static const Renderer rendererOrder[] = {
RENDERER_METAL,
RENDERER_OPENGL,
RENDERER_VULKAN,
};
static std::vector<Renderer> defaultRenderers =
{
RENDERER_METAL,
RENDERER_OPENGL,
RENDERER_VULKAN,
};
static std::vector<Renderer> _renderers = defaultRenderers;
@@ -148,16 +153,20 @@ Graphics *Graphics::createInstance()
{
for (auto r : rendererOrder)
{
if (std::find(_renderers.begin(), _renderers.end(), r) == _renderers.end())
continue;
#ifdef LOVE_GRAPHICS_VULKAN
if (r == RENDERER_VULKAN)
instance = vulkan::createInstance();
#endif
if (r == RENDERER_OPENGL)
instance = opengl::createInstance();
#ifdef LOVE_GRAPHICS_METAL
if (r == RENDERER_METAL)
instance = metal::createInstance();
#endif
if (instance != nullptr)
break;
}
@@ -2548,6 +2557,7 @@ STRINGMAP_CLASS_END(Graphics, Graphics::StackType, Graphics::STACK_MAX_ENUM, sta
STRINGMAP_BEGIN(Renderer, RENDERER_MAX_ENUM, renderer)
{
{ "opengl", RENDERER_OPENGL },
{ "vulkan", RENDERER_VULKAN },
{ "metal", RENDERER_METAL },
}
STRINGMAP_END(Renderer, RENDERER_MAX_ENUM, renderer)
+1
View File
@@ -71,6 +71,7 @@ enum Renderer
RENDERER_NONE,
RENDERER_OPENGL,
RENDERER_METAL,
RENDERER_VULKAN,
RENDERER_MAX_ENUM
};
+5 -4
View File
@@ -244,9 +244,9 @@ static const char vertex_header[] = R"(
static const char vertex_functions[] = R"()";
static const char vertex_main[] = R"(
attribute vec4 VertexPosition;
attribute vec4 VertexTexCoord;
attribute vec4 VertexColor;
LOVE_IO_LOCATION(0) attribute vec4 VertexPosition;
LOVE_IO_LOCATION(1) attribute vec4 VertexTexCoord;
LOVE_IO_LOCATION(2) attribute vec4 VertexColor;
varying vec4 VaryingTexCoord;
varying vec4 VaryingColor;
@@ -540,9 +540,9 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage,
std::stringstream ss;
ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n";
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
if (glsl1on3)
ss << "#define LOVE_GLSL1_ON_GLSL3 1\n";
if (isGammaCorrect())
ss << "#define LOVE_GAMMA_CORRECT 1\n";
if (info.usesMRT)
@@ -556,6 +556,7 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage,
for (const auto &def : options.defines)
ss << "#define " + def.first + " " + def.second + "\n";
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << stageinfo.uniforms;
-1
View File
@@ -61,7 +61,6 @@ public:
void readbackInternal(int slice, int mipmap, const Rect &rect, int destwidth, size_t size, void *dest);
private:
void createTexture();
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
+1
View File
@@ -59,6 +59,7 @@ enum BufferUsage
BUFFERUSAGE_VERTEX = 0,
BUFFERUSAGE_INDEX,
BUFFERUSAGE_TEXEL,
BUFFERUSAGE_UNIFORM,
BUFFERUSAGE_SHADER_STORAGE,
BUFFERUSAGE_MAX_ENUM
};
+233
View File
@@ -0,0 +1,233 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Buffer.h"
#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;
case BUFFERUSAGE_TEXEL: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
case BUFFERUSAGE_SHADER_STORAGE: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
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)
, vgfx(dynamic_cast<Graphics*>(gfx))
, zeroInitialize(settings.zeroInitialize)
{
loadVolatile();
}
bool Buffer::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | getVulkanUsageFlags(usageFlags);
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
if (dataUsage == BUFFERDATAUSAGE_READBACK)
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
else if ((bufferInfo.usage | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) || (bufferInfo.usage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT))
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
auto result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
if (result != VK_SUCCESS)
throw love::Exception("failed to create buffer");
if (zeroInitialize)
vkCmdFillBuffer(vgfx->getCommandBufferForDataTransfer(), buffer, 0, VK_WHOLE_SIZE, 0);
if (usageFlags & BUFFERUSAGEFLAG_TEXEL)
{
VkBufferViewCreateInfo bufferViewInfo{};
bufferViewInfo.buffer = buffer;
bufferViewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
bufferViewInfo.format = Vulkan::getVulkanVertexFormat(getDataMember(0).decl.format);
bufferViewInfo.range = VK_WHOLE_SIZE;
if (vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView) != VK_SUCCESS)
throw love::Exception("failed to create texel buffer view");
}
return true;
}
void Buffer::unloadVolatile()
{
if (buffer == VK_NULL_HANDLE)
return;
auto device = vgfx->getDevice();
vgfx->queueCleanUp(
[device=device, allocator=allocator, buffer=buffer, allocation=allocation, bufferView=bufferView](){
vkDeviceWaitIdle(device);
vmaDestroyBuffer(allocator, buffer, allocation);
if (bufferView)
vkDestroyBufferView(device, bufferView, nullptr);
});
buffer = VK_NULL_HANDLE;
bufferView = VK_NULL_HANDLE;
}
Buffer::~Buffer()
{
unloadVolatile();
}
ptrdiff_t Buffer::getHandle() const
{
return (ptrdiff_t) buffer;
}
ptrdiff_t Buffer::getTexelBufferHandle() const
{
return (ptrdiff_t) bufferView;
}
void *Buffer::map(MapType map, size_t offset, size_t size)
{
if (dataUsage == BUFFERDATAUSAGE_READBACK)
{
char *data = (char*)allocInfo.pMappedData;
return (void*) (data + offset);
}
else
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &stagingAllocInfo) != VK_SUCCESS)
throw love::Exception("failed to create staging buffer");
return stagingAllocInfo.pMappedData;
}
}
bool Buffer::fill(size_t offset, size_t size, const void *data)
{
if (dataUsage == BUFFERDATAUSAGE_READBACK)
{
void *dst = (void*)((char*)allocInfo.pMappedData + offset);
memcpy(dst, data, size);
}
else
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
VkBuffer fillBuffer;
VmaAllocation fillAllocation;
VmaAllocationInfo fillAllocInfo;
if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &fillBuffer, &fillAllocation, &fillAllocInfo) != VK_SUCCESS)
throw love::Exception("failed to create fill buffer");
memcpy(fillAllocInfo.pMappedData, data, size);
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = offset;
bufferCopy.size = size;
vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), fillBuffer, buffer, 1, &bufferCopy);
vgfx->queueCleanUp([allocator = allocator, fillBuffer = fillBuffer, fillAllocation = fillAllocation]() {
vmaDestroyBuffer(allocator, fillBuffer, fillAllocation);
});
}
return true;
}
void Buffer::unmap(size_t usedoffset, size_t usedsize)
{
if (dataUsage != BUFFERDATAUSAGE_READBACK)
{
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = usedoffset;
bufferCopy.size = usedsize;
vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), stagingBuffer, buffer, 1, &bufferCopy);
vgfx->queueCleanUp([allocator = allocator, stagingBuffer = stagingBuffer, stagingAllocation = stagingAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
});
}
}
void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = sourceoffset;
bufferCopy.dstOffset = destoffset;
bufferCopy.size = size;
vkCmdCopyBuffer(commandBuffer, buffer, (VkBuffer) dest->getHandle(), 1, &bufferCopy);
}
} // vulkan
} // graphics
} // love
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Buffer.h"
#include "graphics/Volatile.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Buffer final
: 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;
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;
ptrdiff_t getTexelBufferHandle() const override;
private:
bool zeroInitialize;
VkBuffer buffer = VK_NULL_HANDLE;
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VkBufferView bufferView = VK_NULL_HANDLE;
Graphics *vgfx = nullptr;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocation stagingAllocation;
VmaAllocationInfo allocInfo;
VmaAllocationInfo stagingAllocInfo;
BufferUsageFlags usageFlags;
};
} // vulkan
} // graphics
} // love
File diff suppressed because it is too large Load Diff
+449
View File
@@ -0,0 +1,449 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
// löve
#include "common/config.h"
#include "graphics/Graphics.h"
#include "StreamBuffer.h"
#include "ShaderStage.h"
#include "Shader.h"
#include "Texture.h"
// libraries
#include "VulkanWrapper.h"
#include "libraries/xxHash/xxhash.h"
// c++
#include <iostream>
#include <memory>
#include <functional>
#include <set>
namespace love
{
namespace graphics
{
namespace vulkan
{
struct RenderPassAttachment
{
VkFormat format = VK_FORMAT_UNDEFINED;
bool discard = true;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
bool operator==(const RenderPassAttachment &attachment) const
{
return format == attachment.format &&
discard == attachment.discard &&
msaaSamples == attachment.msaaSamples;
}
};
struct RenderPassConfiguration
{
std::vector<RenderPassAttachment> colorAttachments;
struct StaticRenderPassConfiguration
{
RenderPassAttachment depthAttachment;
bool resolve = false;
} staticData;
bool operator==(const RenderPassConfiguration &conf) const
{
return colorAttachments == conf.colorAttachments &&
(memcmp(&staticData, &conf.staticData, sizeof(StaticRenderPassConfiguration)) == 0);
}
};
struct RenderPassConfigurationHasher
{
size_t operator()(const RenderPassConfiguration &configuration) const
{
size_t hashes[] = {
XXH32(configuration.colorAttachments.data(), configuration.colorAttachments.size() * sizeof(VkFormat), 0),
XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
};
return XXH32(hashes, sizeof(hashes), 0);
}
};
struct FramebufferConfiguration
{
std::vector<VkImageView> colorViews;
struct StaticFramebufferConfiguration
{
VkImageView depthView = VK_NULL_HANDLE;
VkImageView resolveView = VK_NULL_HANDLE;
uint32_t width = 0;
uint32_t height = 0;
VkRenderPass renderPass = VK_NULL_HANDLE;
} staticData;
bool operator==(const FramebufferConfiguration &conf) const
{
return colorViews == conf.colorViews &&
(memcmp(&staticData, &conf.staticData, sizeof(StaticFramebufferConfiguration)) == 0);
}
};
struct FramebufferConfigurationHasher
{
size_t operator()(const FramebufferConfiguration &configuration) const
{
size_t hashes[] = {
XXH32(configuration.colorViews.data(), configuration.colorViews.size() * sizeof(VkImageView), 0),
XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
};
return XXH32(hashes, sizeof(hashes), 0);
}
};
struct OptionalInstanceExtensions
{
bool physicalDeviceProperties2 = false;
};
struct OptionalDeviceFeatures
{
// VK_EXT_extended_dynamic_state
bool extendedDynamicState = false;
// VK_KHR_get_memory_requirements2
bool memoryRequirements2 = false;
// VK_KHR_dedicated_allocation
bool dedicatedAllocation = false;
// VK_KHR_buffer_device_address
bool bufferDeviceAddress = false;
// VK_EXT_memory_budget
bool memoryBudget = false;
// VK_KHR_shader_float_controls
bool shaderFloatControls = false;
// VK_KHR_spirv_1_4
bool spirv14 = false;
};
struct GraphicsPipelineConfiguration
{
VkRenderPass renderPass;
VertexAttributes vertexAttributes;
Shader *shader = nullptr;
bool wireFrame;
BlendState blendState;
ColorChannelMask colorChannelMask;
VkSampleCountFlagBits msaaSamples;
uint32_t numColorAttachments;
PrimitiveType primitiveType;
struct DynamicState
{
CullMode cullmode = CULL_NONE;
Winding winding = WINDING_MAX_ENUM;
StencilAction stencilAction = STENCIL_MAX_ENUM;
CompareMode stencilCompare = COMPARE_MAX_ENUM;
DepthState depthState{};
} dynamicState;
GraphicsPipelineConfiguration()
{
memset(this, 0, sizeof(GraphicsPipelineConfiguration));
}
bool operator==(const GraphicsPipelineConfiguration &other) const
{
return memcmp(this, &other, sizeof(GraphicsPipelineConfiguration)) == 0;
}
};
struct GraphicsPipelineConfigurationHasher
{
size_t operator() (const GraphicsPipelineConfiguration &configuration) const
{
return XXH32(&configuration, sizeof(GraphicsPipelineConfiguration), 0);
}
};
struct BatchedDrawBuffers
{
StreamBuffer *vertexBuffer1;
StreamBuffer *vertexBuffer2;
StreamBuffer *indexBuffer;
StreamBuffer *constantColorBuffer;
~BatchedDrawBuffers()
{
delete vertexBuffer1;
delete vertexBuffer2;
delete indexBuffer;
delete constantColorBuffer;
}
};
struct QueueFamilyIndices
{
Optional<uint32_t> graphicsFamily;
Optional<uint32_t> presentFamily;
bool isComplete() const
{
return graphicsFamily.hasValue && presentFamily.hasValue;
}
};
struct SwapChainSupportDetails
{
VkSurfaceCapabilitiesKHR capabilities{};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
struct RenderpassState
{
bool active = false;
VkRenderPassBeginInfo beginInfo{};
bool useConfigurations = false;
RenderPassConfiguration renderPassConfiguration{};
FramebufferConfiguration framebufferConfiguration{};
VkPipeline pipeline = VK_NULL_HANDLE;
std::vector<VkImage> transitionImages;
uint32_t numColorAttachments = 0;
float width = 0.0f;
float height = 0.0f;
VkSampleCountFlagBits msaa = VK_SAMPLE_COUNT_1_BIT;
};
struct ScreenshotReadbackBuffer
{
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
VkImage image;
VmaAllocation imageAllocation;
};
class Graphics final : public love::graphics::Graphics
{
public:
Graphics();
~Graphics();
const char *getName() const override;
const VkDevice getDevice() 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) 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;
int getBackbufferMSAA() const override;
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) 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;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset) override;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty) override;
// internal functions.
VkCommandBuffer getCommandBufferForDataTransfer();
void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(std::function<void()> callback);
void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr);
uint32_t getNumImagesInFlight() const;
uint32_t getFrameIndex() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
graphics::Texture *getDefaultTexture() const;
VkSampler getCachedSampler(const SamplerState &sampler);
void setComputeShader(Shader *computeShader);
std::set<Shader*> &getUsedShadersInFrame();
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
const OptionalDeviceFeatures &getEnabledOptionalDeviceExtensions() const;
VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
protected:
graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
graphics::Shader *newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
graphics::StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override;
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);
VkCompositeAlphaFlagBitsKHR chooseCompositeAlpha(const VkSurfaceCapabilitiesKHR &capabilities);
void createSwapChain();
void createImageViews();
void createScreenshotCallbackBuffers();
void createDefaultRenderPass();
void createDefaultFramebuffers();
VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration);
VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration);
void createDefaultShaders();
VkRenderPass createRenderPass(RenderPassConfiguration &configuration);
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration &configuration);
void createColorResources();
VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
VkFormat findDepthFormat();
void createDepthResources();
void createCommandPool();
void createCommandBuffers();
void createSyncObjects();
void createDefaultTexture();
void cleanup();
void cleanupSwapChain();
void recreateSwapChain();
void initDynamicState();
void beginFrame();
void startRecordingGraphicsCommands(bool newFrame);
void endRecordingGraphicsCommands(bool present);
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration &configuration);
void updatedBatchedDrawBuffers();
bool usesConstantVertexColor(const VertexAttributes &attribs);
void createVulkanVertexFormat(
VertexAttributes vertexAttributes,
std::vector<VkVertexInputBindingDescription> &bindingDescriptions,
std::vector<VkVertexInputAttributeDescription> &attributeDescriptions);
void prepareDraw(
const VertexAttributes &attributes,
const BufferBindings &buffers, graphics::Texture *texture,
PrimitiveType, CullMode);
void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture);
void setDefaultRenderPass();
void startRenderPass();
void endRenderPass();
VkSampler createSampler(const SamplerState &sampler);
void cleanupUnusedObjects();
uint32_t vulkanApiVersion = VK_VERSION_1_0;
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
bool windowHasStencil = false;
int requestedMsaa = 0;
VkDevice device = VK_NULL_HANDLE;
OptionalInstanceExtensions optionalInstanceExtensions;
OptionalDeviceFeatures optionalDeviceFeatures;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
VkSurfaceTransformFlagBitsKHR preTransform = {};
Matrix4 displayRotation;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
VkExtent2D swapChainExtent = VkExtent2D();
std::vector<VkImageView> swapChainImageViews;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
VkImage colorImage = VK_NULL_HANDLE;
VkImageView colorImageView = VK_NULL_HANDLE;
VmaAllocation colorImageAllocation = VK_NULL_HANDLE;
VkImage depthImage = VK_NULL_HANDLE;
VkImageView depthImageView = VK_NULL_HANDLE;
VmaAllocation depthImageAllocation = VK_NULL_HANDLE;
VkRenderPass defaultRenderPass = VK_NULL_HANDLE;
std::vector<VkFramebuffer> defaultFramebuffers;
std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers;
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
std::unordered_map<VkRenderPass, bool> renderPassUsages;
std::unordered_map<VkFramebuffer, bool> framebufferUsages;
std::unordered_map<VkPipeline, bool> pipelineUsages;
std::unordered_map<uint64, VkSampler> samplers;
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
Shader* computeShader = nullptr;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment = 0;
bool imageRequested = false;
uint32_t frameCounter = 0;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
bool transitionColorDepthLayouts = false;
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
std::unique_ptr<Texture> standardTexture = 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;
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
std::vector<ScreenshotReadbackBuffer> screenshotReadbackBuffers;
std::set<Shader*> usedShadersInFrame;
RenderpassState renderPassState;
};
} // vulkan
} // graphics
} // love
@@ -0,0 +1,101 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "GraphicsReadback.h"
#include "Buffer.h"
#include "Texture.h"
#include "Graphics.h"
#include "data/ByteData.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)
: graphics::GraphicsReadback(gfx, method, buffer, offset, size, dest, destoffset)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
// Immediate readback of readback-type buffers doesn't need a staging buffer.
if (method != READBACK_IMMEDIATE || buffer->getDataUsage() != BUFFERDATAUSAGE_READBACK)
{
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
gfx->copyBuffer(buffer, stagingBuffer, offset, 0, size);
}
if (method == READBACK_IMMEDIATE)
{
vgfx->submitGpuCommands(false);
if (stagingBuffer.get()) {
status = readbackBuffer(stagingBuffer, 0, size);
gfx->releaseTemporaryBuffer(stagingBuffer);
}
else
status = readbackBuffer(buffer, offset, size);
}
else
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
}
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty)
: graphics::GraphicsReadback(gfx, method, texture, slice, mipmap, rect, dest, destx, desty)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
size_t size = getPixelFormatSliceSize(textureFormat, rect.w, rect.h);
stagingBuffer = vgfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
vgfx->copyTextureToBuffer(texture, stagingBuffer, slice, mipmap, rect, 0, 0);
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
if (method == READBACK_IMMEDIATE)
vgfx->submitGpuCommands(false);
}
GraphicsReadback::~GraphicsReadback()
{
}
void GraphicsReadback::wait()
{
if (status == STATUS_WAITING)
vgfx->submitGpuCommands(false);
}
void GraphicsReadback::update()
{
}
} // vulkan
} // graphics
} // love
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/GraphicsReadback.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class GraphicsReadback final : public graphics::GraphicsReadback
{
public:
GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty);
virtual ~GraphicsReadback();
void wait() override;
void update() override;
private:
Graphics *vgfx = nullptr;
StrongRef<love::graphics::Buffer> stagingBuffer;
};
} // vulkan
} // graphics
} // love
File diff suppressed because it is too large Load Diff
+153
View File
@@ -0,0 +1,153 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
// LÖVE
#include "common/Optional.h"
#include "graphics/Shader.h"
#include "graphics/vulkan/ShaderStage.h"
#include "Vulkan.h"
// Libraries
#include "VulkanWrapper.h"
#include "libraries/spirv_cross/spirv_reflect.hpp"
// C++
#include <map>
#include <memory>
#include <unordered_map>
#include <queue>
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Shader final
: public graphics::Shader
, public Volatile
{
public:
Shader(StrongRef<love::graphics::ShaderStage> stages[]);
virtual ~Shader();
bool loadVolatile() override;
void unloadVolatile() override;
VkPipeline getComputePipeline() const;
const std::vector<VkPipelineShaderStageCreateInfo> &getShaderStages() const;
const VkPipelineLayout getGraphicsPipelineLayout() const;
void newFrame(uint32_t frameIndex);
void cmdPushDescriptorSets(VkCommandBuffer, VkPipelineBindPoint);
void attach() override;
ptrdiff_t getHandle() const { return 0; }
std::string getWarnings() const override { return ""; }
int getVertexAttributeIndex(const std::string &name) override;
const UniformInfo *getUniformInfo(const std::string &name) const override;
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const 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;
bool hasUniform(const std::string &name) const override;
void setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) override;
void setMainTex(graphics::Texture *texture);
private:
void calculateUniformBufferSizeAligned();
void compileShaders();
void createDescriptorSetLayout();
void createPipelineLayout();
void createDescriptorPoolSizes();
void createStreamBuffers();
void buildLocalUniforms(
spirv_cross::Compiler &comp,
const spirv_cross::SPIRType &type,
size_t baseoff,
const std::string &basename);
void initDescriptorSet();
void updateUniform(const UniformInfo* info, int count, bool internal);
VkDescriptorSet allocateDescriptorSet();
VkDeviceSize uniformBufferSizeAligned;
VkPipeline computePipeline;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
// we don't know how much memory we need per frame for the uniform buffer descriptors
// we keep a vector of stream buffers per frame in flight
// that gets dynamically increased if more memory is needed
std::vector<std::vector<StreamBuffer*>> streamBuffers;
std::vector<VkDescriptorPool> descriptorPools;
std::queue<VkDescriptorSet> freeDescriptorSets;
std::vector<std::vector<VkDescriptorSet>> descriptorSetsVector;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> shaderModules;
Graphics *vgfx = nullptr;
VkDevice device;
bool isCompute = false;
std::unordered_map<std::string, graphics::Shader::UniformInfo> uniformInfos;
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
std::vector<uint8> localUniformData;
std::vector<uint8> localUniformStagingData;
uint32_t uniformLocation;
OptionalInt builtinUniformDataOffset;
std::unordered_map<std::string, int> attributes;
VkDescriptorSet currentDescriptorSet;
uint32_t currentFrame;
uint32_t currentUsedUniformStreamBuffersCount;
uint32_t currentUsedDescriptorSetsCount;
};
}
}
}
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "ShaderStage.h"
#include "Graphics.h"
#include "libraries/glslang/glslang/Public/ShaderLang.h"
#include "libraries/glslang/SPIRV/GlslangToSpv.h"
#include <fstream>
#include <cstdio>
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.
}
ptrdiff_t ShaderStage::getHandle() const
{
return 0;
}
} // love
} // graphics
} // vulkan
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/ShaderStage.h"
#include "modules/graphics/Graphics.h"
#include "VulkanWrapper.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);
ptrdiff_t getHandle() const override;
};
}
}
}
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "StreamBuffer.h"
#include "Graphics.h"
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)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
loadVolatile();
}
bool StreamBuffer::loadVolatile()
{
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;
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
usedGPUMemory = 0;
return true;
}
void StreamBuffer::unloadVolatile()
{
if (buffer == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
StreamBuffer::~StreamBuffer()
{
unloadVolatile();
}
ptrdiff_t StreamBuffer::getHandle() const
{
return (ptrdiff_t) buffer;
}
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
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Volatile.h"
#include "graphics/StreamBuffer.h"
#include "graphics/Graphics.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class StreamBuffer final
: public love::graphics::StreamBuffer
, public graphics::Volatile
{
public:
StreamBuffer(graphics::Graphics *gfx, BufferUsage mode, size_t size);
virtual ~StreamBuffer();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
MapInfo map(size_t minsize) override;
size_t unmap(size_t usedSize) override;
void markUsed(size_t usedSize) override;
void nextFrame() override;
ptrdiff_t getHandle() const override;
private:
Graphics *vgfx = nullptr;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
VkBuffer buffer = VK_NULL_HANDLE;
size_t usedGPUMemory;
};
} // vulkan
} // graphics
} // love
+589
View File
@@ -0,0 +1,589 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Texture.h"
#include "Graphics.h"
#include "Vulkan.h"
#include <limits>
namespace love
{
namespace graphics
{
namespace vulkan
{
Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data)
: love::graphics::Texture(gfx, settings, data)
, vgfx(dynamic_cast<Graphics*>(gfx))
, slices(settings.type)
, imageAspect(0)
{
if (data)
slices = *data;
loadVolatile();
}
bool Texture::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
if (isPixelFormatDepthStencil(format))
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
else if (isPixelFormatDepth(format))
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
else
imageAspect |= VK_IMAGE_ASPECT_COLOR_BIT;
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageUsageFlags usageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (readable)
{
if (!isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
if (!isPixelFormatCompressed(format) && !isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
}
if (renderTarget)
{
if (isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
else
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
VkImageCreateFlags createFlags = 0;
layerCount = 1;
if (texType == TEXTURE_2D_ARRAY)
layerCount = getLayerCount();
else if (texType == TEXTURE_CUBE)
{
layerCount = 6;
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = createFlags;
imageInfo.imageType = Vulkan::getImageType(getTextureType());
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
imageInfo.extent.depth = static_cast<uint32_t>(depth);
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
imageInfo.format = vulkanFormat.internalFormat;
if (isPixelFormatCompressed(format))
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
else
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = msaaSamples;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
throw love::Exception("failed to create image");
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (isPixelFormatDepthStencil(format))
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
else if (computeWrite)
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
else
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
bool hasdata = slices.get(0, 0) != nullptr;
if (hasdata)
for (int mip = 0; mip < getMipmapCount(); mip++)
{
int sliceCount;
if (texType == TEXTURE_CUBE)
sliceCount = 6;
else
sliceCount = slices.getSliceCount();
for (int slice = 0; slice < sliceCount; slice++)
{
auto id = slices.get(slice, mip);
if (id != nullptr)
uploadImageData(id, mip, slice, 0, 0);
}
}
else
clear();
createTextureImageView();
textureSampler = vgfx->getCachedSampler(samplerState);
if (!isPixelFormatDepthStencil(format) && mipmapCount > 1 && getMipmapsMode() != MIPMAPS_NONE)
generateMipmaps();
if (renderTarget)
{
renderTargetImageViews.resize(getMipmapCount());
for (int mip = 0; mip < getMipmapCount(); mip++)
{
renderTargetImageViews.at(mip).resize(layerCount);
for (int slice = 0; slice < layerCount; slice++)
{
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = mip;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = slice;
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, &renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS)
throw love::Exception("could not create render target image view");
}
}
}
return true;
}
void Texture::unloadVolatile()
{
if (textureImage == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([
device = device,
textureImageView = textureImageView,
allocator = allocator,
textureImage = textureImage,
textureImageAllocation = textureImageAllocation,
textureImageViews = std::move(renderTargetImageViews)] () {
vkDestroyImageView(device, textureImageView, nullptr);
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
for (const auto &views : textureImageViews)
for (const auto &view : views)
vkDestroyImageView(device, view, nullptr);
});
textureImage = VK_NULL_HANDLE;
}
Texture::~Texture()
{
unloadVolatile();
}
ptrdiff_t Texture::getRenderTargetHandle() const
{
return (ptrdiff_t)textureImageView;
}
ptrdiff_t Texture::getSamplerHandle() const
{
return (ptrdiff_t)textureSampler;
}
VkImageView Texture::getRenderTargetView(int mip, int layer)
{
return renderTargetImageViews.at(mip).at(layer);
}
VkSampleCountFlagBits Texture::getMsaaSamples() const
{
return msaaSamples;
}
int Texture::getMSAA() const
{
return static_cast<int>(msaaSamples);
}
ptrdiff_t Texture::getHandle() const
{
return (ptrdiff_t)textureImage;
}
void Texture::setSamplerState(const SamplerState &s)
{
love::graphics::Texture::setSamplerState(s);
textureSampler = vgfx->getCachedSampler(s);
}
VkImageLayout Texture::getImageLayout() const
{
return imageLayout;
}
void Texture::createTextureImageView()
{
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = getMipmapCount();
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = layerCount;
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::clear()
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceRange range{};
range.aspectMask = imageAspect;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
if (imageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
auto clearColor = getClearValue();
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
}
else if (imageLayout == VK_IMAGE_LAYOUT_GENERAL)
{
auto clearColor = getClearValue();
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range);
}
else
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
VkClearDepthStencilValue depthStencilColor{};
depthStencilColor.depth = 0.0f;
depthStencilColor.stencil = 0;
vkCmdClearDepthStencilImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &depthStencilColor, 1, &range);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
}
}
VkClearColorValue Texture::getClearValue()
{
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkClearColorValue clearColor{};
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::generateMipmapsInternal()
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, static_cast<uint32_t>(getMipmapCount()), 0, static_cast<uint32_t>(layerCount));
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = textureImage;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = static_cast<uint32_t>(layerCount);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1u;
uint32_t mipLevels = static_cast<uint32_t>(getMipmapCount());
for (uint32_t i = 1; i < mipLevels; i++)
{
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { getPixelWidth(i - 1), getPixelHeight(i - 1), 1 };
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = static_cast<uint32_t>(layerCount);
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { getPixelWidth(i), getPixelHeight(i), 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = static_cast<uint32_t>(layerCount);
vkCmdBlitImage(commandBuffer,
textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &blit,
VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
}
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
}
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);
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
uint32_t baseLayer;
if (getTextureType() == TEXTURE_VOLUME)
baseLayer = 0;
else
baseLayer = slice;
region.imageSubresource.aspectMask = imageAspect;
region.imageSubresource.mipLevel = level;
region.imageSubresource.baseArrayLayer = baseLayer;
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
};
if (getTextureType() == TEXTURE_VOLUME)
region.imageOffset.z = slice;
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
level, 1, baseLayer, 1);
vkCmdCopyBufferToImage(
commandBuffer,
stagingBuffer,
textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
level, 1, baseLayer, 1);
}
else
vkCmdCopyBufferToImage(
commandBuffer,
stagingBuffer,
textureImage,
imageLayout,
1,
&region
);
vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
});
}
void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.layerCount = 1;
VkBufferImageCopy region{};
region.bufferOffset = sourceoffset;
region.bufferRowLength = sourcewidth;
region.bufferImageHeight = 1;
region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w);
region.imageExtent.height = static_cast<uint32_t>(rect.h);
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
else
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
}
void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.layerCount = 1;
VkBufferImageCopy region{};
region.bufferOffset = destoffset;
region.bufferRowLength = destwidth;
region.bufferImageHeight = 0;
region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w);
region.imageExtent.height = static_cast<uint32_t>(rect.h);
region.imageExtent.depth = 1;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, &region);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout);
}
else
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, &region);
}
} // vulkan
} // graphics
} // love
+92
View File
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Texture final
: 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;
void setSamplerState(const SamplerState &s) override;
VkImageLayout getImageLayout() const;
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;
ptrdiff_t getSamplerHandle() const override;
VkImageView getRenderTargetView(int mip, int layer);
VkSampleCountFlagBits getMsaaSamples() const;
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
void generateMipmapsInternal() override;
int getMSAA() const override;
ptrdiff_t getHandle() const override;
private:
void createTextureImageView();
void clear();
VkClearColorValue getClearValue();
Graphics *vgfx = nullptr;
VkDevice device = VK_NULL_HANDLE;
VkImageAspectFlags imageAspect;
VmaAllocator allocator = VK_NULL_HANDLE;
VkImage textureImage = VK_NULL_HANDLE;
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VmaAllocation textureImageAllocation = VK_NULL_HANDLE;
VkImageView textureImageView = VK_NULL_HANDLE;
std::vector<std::vector<VkImageView>> renderTargetImageViews;
VkSampler textureSampler = VK_NULL_HANDLE;
Slices slices;
int layerCount = 0;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
};
} // vulkan
} // graphics
} // love
+990
View File
@@ -0,0 +1,990 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Vulkan.h"
#include <sstream>
namespace love
{
namespace graphics
{
namespace vulkan
{
static uint32_t numShaderSwitches;
static int vsync = 1;
void Vulkan::shaderSwitch()
{
numShaderSwitches++;
}
uint32_t Vulkan::getNumShaderSwitches()
{
return numShaderSwitches;
}
void Vulkan::resetShaderSwitches()
{
numShaderSwitches = 0;
}
void Vulkan::setVsync(int value)
{
vsync = value;
}
int Vulkan::getVsync()
{
return vsync;
}
uint32_t Vulkan::getSupportedVulkanApiVersion(uint32_t suggested)
{
#ifdef VK_VERSION_1_3
if (suggested >= VK_API_VERSION_1_3)
return VK_API_VERSION_1_3;
#endif
#ifdef VK_VERSION_1_2
if (suggested >= VK_API_VERSION_1_2)
return VK_API_VERSION_1_2;
#endif
#ifdef VK_VERSION_1_1
if (suggested >= VK_API_VERSION_1_1)
return VK_API_VERSION_1_1;
#endif
return VK_API_VERSION_1_0;
}
VkFormat Vulkan::getVulkanVertexFormat(DataFormat format)
{
switch (format)
{
case DATAFORMAT_FLOAT:
return VK_FORMAT_R32_SFLOAT;
case DATAFORMAT_FLOAT_VEC2:
return VK_FORMAT_R32G32_SFLOAT;
case DATAFORMAT_FLOAT_VEC3:
return VK_FORMAT_R32G32B32_SFLOAT;
case DATAFORMAT_FLOAT_VEC4:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case DATAFORMAT_FLOAT_MAT2X2:
case DATAFORMAT_FLOAT_MAT2X3:
case DATAFORMAT_FLOAT_MAT2X4:
case DATAFORMAT_FLOAT_MAT3X2:
case DATAFORMAT_FLOAT_MAT3X3:
case DATAFORMAT_FLOAT_MAT3X4:
case DATAFORMAT_FLOAT_MAT4X2:
case DATAFORMAT_FLOAT_MAT4X3:
case DATAFORMAT_FLOAT_MAT4X4:
throw love::Exception("unimplemented data format (matnxm)");
case DATAFORMAT_INT32:
return VK_FORMAT_R32_SINT;
case DATAFORMAT_INT32_VEC2:
return VK_FORMAT_R32G32_SINT;
case DATAFORMAT_INT32_VEC3:
return VK_FORMAT_R32G32B32_SINT;
case DATAFORMAT_INT32_VEC4:
return VK_FORMAT_R32G32B32A32_SINT;
case DATAFORMAT_UINT32:
return VK_FORMAT_R32_UINT;
case DATAFORMAT_UINT32_VEC2:
return VK_FORMAT_R32G32_UINT;
case DATAFORMAT_UINT32_VEC3:
return VK_FORMAT_R32G32B32_UINT;
case DATAFORMAT_UINT32_VEC4:
return VK_FORMAT_R32G32B32A32_UINT;
case DATAFORMAT_SNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_SNORM;
case DATAFORMAT_UNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_UNORM;
case DATAFORMAT_INT8_VEC4:
return VK_FORMAT_R8G8B8A8_SINT;
case DATAFORMAT_UINT8_VEC4:
return VK_FORMAT_R8G8B8A8_UINT;
case DATAFORMAT_SNORM16_VEC2:
return VK_FORMAT_R16G16_SNORM;
case DATAFORMAT_SNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_SNORM;
case DATAFORMAT_UNORM16_VEC2:
return VK_FORMAT_R16G16_UNORM;
case DATAFORMAT_UNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_UNORM;
case DATAFORMAT_INT16_VEC2:
return VK_FORMAT_R16G16_SINT;
case DATAFORMAT_INT16_VEC4:
return VK_FORMAT_R16G16B16A16_SINT;
case DATAFORMAT_UINT16:
return VK_FORMAT_R16_UINT;
case DATAFORMAT_UINT16_VEC2:
return VK_FORMAT_R16G16_UINT;
case DATAFORMAT_UINT16_VEC4:
return VK_FORMAT_R16G16B16A16_UINT;
case DATAFORMAT_BOOL:
case DATAFORMAT_BOOL_VEC2:
case DATAFORMAT_BOOL_VEC3:
case DATAFORMAT_BOOL_VEC4:
throw love::Exception("unimplemented data format (bool)");
default:
throw love::Exception("unknown data format");
}
}
TextureFormat Vulkan::getTextureFormat(PixelFormat format)
{
TextureFormat textureFormat{};
switch (format)
{
case PIXELFORMAT_UNKNOWN:
throw love::Exception("unknown pixel format");
case PIXELFORMAT_NORMAL:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB;
break;
case PIXELFORMAT_HDR:
throw love::Exception("unimplemented pixel format: hdr");
case PIXELFORMAT_R8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8_UNORM;
break;
case PIXELFORMAT_R8_INT:
textureFormat.internalFormat = VK_FORMAT_R8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_R16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16_UNORM;
break;
case PIXELFORMAT_R16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16_SFLOAT;
break;
case PIXELFORMAT_R16_INT:
textureFormat.internalFormat = VK_FORMAT_R16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_R32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32_SFLOAT;
break;
case PIXELFORMAT_R32_INT:
textureFormat.internalFormat = VK_FORMAT_R32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RG8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM;
break;
case PIXELFORMAT_RG8_INT:
textureFormat.internalFormat = VK_FORMAT_R8G8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8G8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_LA8_UNORM: // Same as RG8: but accessed as (L: L: L: A)
textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM;
textureFormat.swizzleR = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleG = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleB = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleA = VK_COMPONENT_SWIZZLE_G;
break;
case PIXELFORMAT_RG16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16G16_UNORM;
break;
case PIXELFORMAT_RG16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16G16_SFLOAT;
break;
case PIXELFORMAT_RG16_INT:
textureFormat.internalFormat = VK_FORMAT_R16G16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16G16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RG32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32G32_SFLOAT;
break;
case PIXELFORMAT_RG32_INT:
textureFormat.internalFormat = VK_FORMAT_R32G32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32G32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UNORM;
break;
case PIXELFORMAT_RGBA8_UNORM_sRGB:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB;
break;
case PIXELFORMAT_BGRA8_UNORM:
textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_UNORM;
break;
case PIXELFORMAT_BGRA8_UNORM_sRGB:
textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_SRGB;
break;
case PIXELFORMAT_RGBA8_INT:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UNORM;
break;
case PIXELFORMAT_RGBA16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
break;
case PIXELFORMAT_RGBA16_INT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
break;
case PIXELFORMAT_RGBA32_INT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA4_UNORM: // LSB->MSB: [a: b: g: r]
textureFormat.internalFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
break;
case PIXELFORMAT_RGB5A1_UNORM: // LSB->MSB: [a: b: g: r]
textureFormat.internalFormat = VK_FORMAT_R5G5B5A1_UNORM_PACK16;
break;
case PIXELFORMAT_RGB565_UNORM: // LSB->MSB: [b: g: r]
textureFormat.internalFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
break;
case PIXELFORMAT_RGB10A2_UNORM: // LSB->MSB: [r: g: b: a]
case PIXELFORMAT_RG11B10_FLOAT: // LSB->MSB: [r: g: b]
throw love::Exception("unimplemented pixel format (rgb10a2, rg11b10)");
case PIXELFORMAT_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_S8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH16_UNORM:
textureFormat.internalFormat = VK_FORMAT_D16_UNORM;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH24_UNORM:
case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_D24_UNORM_S8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_D32_SFLOAT;
break;
case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_D32_SFLOAT_S8_UINT;
break;
case PIXELFORMAT_DXT1_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
break;
case PIXELFORMAT_DXT3_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC2_UNORM_BLOCK;
break;
case PIXELFORMAT_DXT5_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC3_UNORM_BLOCK;
break;
case PIXELFORMAT_BC4_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC4_UNORM_BLOCK;
break;
case PIXELFORMAT_BC4_SNORM:
textureFormat.internalFormat = VK_FORMAT_BC4_SNORM_BLOCK;
break;
case PIXELFORMAT_BC5_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC5_UNORM_BLOCK;
break;
case PIXELFORMAT_BC5_SNORM:
textureFormat.internalFormat = VK_FORMAT_BC5_SNORM_BLOCK;
break;
case PIXELFORMAT_BC6H_UFLOAT:
textureFormat.internalFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK;
break;
case PIXELFORMAT_BC6H_FLOAT:
textureFormat.internalFormat = VK_FORMAT_BC6H_SFLOAT_BLOCK;
break;
case PIXELFORMAT_BC7_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC7_UNORM_BLOCK;
break;
case PIXELFORMAT_PVR1_RGB2_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGB4_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGBA2_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGBA4_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_ETC1_UNORM:
throw love::Exception("unimplemented pixel format: etc1");
case PIXELFORMAT_ETC2_RGB_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case PIXELFORMAT_ETC2_RGBA_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
break;
case PIXELFORMAT_ETC2_RGBA1_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_R_UNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_R_SNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11_SNORM_BLOCK;
break;
case PIXELFORMAT_EAC_RG_UNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_RG_SNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
break;
case PIXELFORMAT_ASTC_4x4:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_5x4:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_5x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_6x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_6x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x8:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x8:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x10:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_12x10:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_12x12:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT;
#endif
break;
default:
throw love::Exception("unknown pixel format");
}
return textureFormat;
}
// values taken from https://pcisig.com/membership/member-companies
// as specified at https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html
std::string Vulkan::getVendorName(uint32_t vendorId)
{
switch (vendorId)
{
case 4130:
return "AMD";
case 4318:
return "Nvidia";
case 32902:
return "Intel";
case 4203:
return "Apple";
case 5140:
return "Microsoft";
case 5045:
return "ARM";
case 20803:
return "Qualcomm";
case 5348:
return "Broadcom";
default:
return "unknown";
}
}
std::string Vulkan::getVulkanApiVersion(uint32_t version)
{
std::stringstream ss;
ss << VK_API_VERSION_MAJOR(version)
<< "." << VK_API_VERSION_MINOR(version)
<< "." << VK_API_VERSION_PATCH(version);
return ss.str();
}
VkPrimitiveTopology Vulkan::getPrimitiveTypeTopology(graphics::PrimitiveType primitiveType)
{
switch (primitiveType)
{
case PRIMITIVE_POINTS:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PRIMITIVE_TRIANGLES:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PRIMITIVE_TRIANGLE_FAN:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case PRIMITIVE_TRIANGLE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
default:
throw love::Exception("unknown primitive type");
}
}
VkBlendFactor Vulkan::getBlendFactor(BlendFactor blendFactor)
{
switch (blendFactor)
{
case BLENDFACTOR_ZERO:
return VK_BLEND_FACTOR_ZERO;
case BLENDFACTOR_ONE:
return VK_BLEND_FACTOR_ONE;
case BLENDFACTOR_SRC_COLOR:
return VK_BLEND_FACTOR_SRC_COLOR;
case BLENDFACTOR_ONE_MINUS_SRC_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BLENDFACTOR_SRC_ALPHA:
return VK_BLEND_FACTOR_SRC_ALPHA;
case BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BLENDFACTOR_DST_COLOR:
return VK_BLEND_FACTOR_DST_COLOR;
case BLENDFACTOR_ONE_MINUS_DST_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BLENDFACTOR_DST_ALPHA:
return VK_BLEND_FACTOR_DST_ALPHA;
case BLENDFACTOR_ONE_MINUS_DST_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BLENDFACTOR_SRC_ALPHA_SATURATED:
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
default:
throw love::Exception("unknown blend factor");
}
}
VkBlendOp Vulkan::getBlendOp(BlendOperation op)
{
switch (op)
{
case BLENDOP_ADD:
return VK_BLEND_OP_ADD;
case BLENDOP_MAX:
return VK_BLEND_OP_MAX;
case BLENDOP_MIN:
return VK_BLEND_OP_MIN;
case BLENDOP_SUBTRACT:
return VK_BLEND_OP_SUBTRACT;
case BLENDOP_REVERSE_SUBTRACT:
return VK_BLEND_OP_REVERSE_SUBTRACT;
default:
throw love::Exception("unknown blend operation");
}
}
VkBool32 Vulkan::getBool(bool b)
{
if (b)
return VK_TRUE;
else
return VK_FALSE;
}
VkColorComponentFlags Vulkan::getColorMask(ColorChannelMask mask)
{
VkColorComponentFlags flags = 0;
if (mask.r)
flags |= VK_COLOR_COMPONENT_R_BIT;
if (mask.g)
flags |= VK_COLOR_COMPONENT_G_BIT;
if (mask.b)
flags |= VK_COLOR_COMPONENT_B_BIT;
if (mask.a)
flags |= VK_COLOR_COMPONENT_A_BIT;
return flags;
}
VkFrontFace Vulkan::getFrontFace(Winding winding)
{
switch (winding)
{
case WINDING_CW:
return VK_FRONT_FACE_CLOCKWISE;
case WINDING_CCW:
return VK_FRONT_FACE_COUNTER_CLOCKWISE;
default:
throw love::Exception("unknown winding");
}
}
VkCullModeFlags Vulkan::getCullMode(CullMode cullmode)
{
switch (cullmode)
{
case CULL_BACK:
return VK_CULL_MODE_BACK_BIT;
case CULL_FRONT:
return VK_CULL_MODE_FRONT_BIT;
case CULL_NONE:
return VK_CULL_MODE_NONE;
default:
throw love::Exception("unknown cull mode");
}
}
VkImageType Vulkan::getImageType(TextureType textureType)
{
switch (textureType)
{
case TEXTURE_2D:
case TEXTURE_2D_ARRAY:
case TEXTURE_CUBE:
return VK_IMAGE_TYPE_2D;
case TEXTURE_VOLUME:
return VK_IMAGE_TYPE_3D;
default:
throw love::Exception("unknown texture type");
}
}
VkImageViewType Vulkan::getImageViewType(TextureType textureType)
{
switch (textureType)
{
case TEXTURE_2D:
return VK_IMAGE_VIEW_TYPE_2D;
case TEXTURE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case TEXTURE_CUBE:
return VK_IMAGE_VIEW_TYPE_CUBE;
case TEXTURE_VOLUME:
return VK_IMAGE_VIEW_TYPE_3D;
default:
throw love::Exception("unknown texture type");
}
}
VkPolygonMode Vulkan::getPolygonMode(bool wireframe)
{
if (wireframe)
return VK_POLYGON_MODE_LINE;
else
return VK_POLYGON_MODE_FILL;
}
VkFilter Vulkan::getFilter(SamplerState::FilterMode mode)
{
switch (mode)
{
case SamplerState::FILTER_LINEAR:
return VK_FILTER_LINEAR;
case SamplerState::FILTER_NEAREST:
return VK_FILTER_NEAREST;
default:
throw love::Exception("unkonwn filter mode");
}
}
VkSamplerAddressMode Vulkan::getWrapMode(SamplerState::WrapMode mode)
{
switch (mode)
{
//fixme: not accounting for different clamps (how does that work in vulkan?)
case SamplerState::WRAP_CLAMP:
case SamplerState::WRAP_CLAMP_ZERO:
case SamplerState::WRAP_CLAMP_ONE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerState::WRAP_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerState::WRAP_MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
default:
throw love::Exception("unknown wrap mode");
}
}
VkCompareOp Vulkan::getCompareOp(CompareMode mode)
{
switch (mode)
{
case COMPARE_LESS:
return VK_COMPARE_OP_LESS;
case COMPARE_LEQUAL:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case COMPARE_EQUAL:
return VK_COMPARE_OP_EQUAL;
case COMPARE_GEQUAL:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case COMPARE_GREATER:
return VK_COMPARE_OP_GREATER;
case COMPARE_NOTEQUAL:
return VK_COMPARE_OP_NOT_EQUAL;
case COMPARE_ALWAYS:
return VK_COMPARE_OP_ALWAYS;
case COMPARE_NEVER:
return VK_COMPARE_OP_NEVER;
default:
throw love::Exception("unknown compare mode");
}
}
VkSamplerMipmapMode Vulkan::getMipMapMode(SamplerState::MipmapFilterMode mode)
{
switch (mode)
{
case SamplerState::MIPMAP_FILTER_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerState::MIPMAP_FILTER_NONE:
case SamplerState::MIPMAP_FILTER_LINEAR:
default:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
VkDescriptorType Vulkan::getDescriptorType(graphics::Shader::UniformType type)
{
switch (type)
{
case graphics::Shader::UniformType::UNIFORM_FLOAT:
case graphics::Shader::UniformType::UNIFORM_MATRIX:
case graphics::Shader::UniformType::UNIFORM_INT:
case graphics::Shader::UniformType::UNIFORM_UINT:
case graphics::Shader::UniformType::UNIFORM_BOOL:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case graphics::Shader::UniformType::UNIFORM_SAMPLER:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case graphics::Shader::UniformType::UNIFORM_STORAGETEXTURE:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
case graphics::Shader::UniformType::UNIFORM_TEXELBUFFER:
return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
case graphics::Shader::UniformType::UNIFORM_STORAGEBUFFER:
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
default:
throw love::Exception("unkonwn uniform type");
}
}
VkStencilOp Vulkan::getStencilOp(StencilAction action)
{
switch (action)
{
case STENCIL_KEEP:
return VK_STENCIL_OP_KEEP;
case STENCIL_ZERO:
return VK_STENCIL_OP_ZERO;
case STENCIL_REPLACE:
return VK_STENCIL_OP_REPLACE;
case STENCIL_INCREMENT:
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case STENCIL_DECREMENT:
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case STENCIL_INCREMENT_WRAP:
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case STENCIL_DECREMENT_WRAP:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
case STENCIL_INVERT:
return VK_STENCIL_OP_INVERT;
default:
throw love::Exception("unknown stencil action");
}
}
VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
{
switch (type)
{
case INDEX_UINT16: return VK_INDEX_TYPE_UINT16;
case INDEX_UINT32: return VK_INDEX_TYPE_UINT32;
default:
throw love::Exception("unknown Index Data type");
}
}
void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount)
{
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = baseLevel;
barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = baseLayer;
barrier.subresourceRange.layerCount = layerCount;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
// we use general for images that are both sampled and compute write
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_UNDEFINED)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else
throw std::invalid_argument("unsupported layout transition!");
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
}
} // vulkan
} // graphics
} // love
+92
View File
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Graphics.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
enum InternalFormatRepresentation
{
FORMATREPRESENTATION_FLOAT,
FORMATREPRESENTATION_UINT,
FORMATREPRESENTATION_SINT,
FORMATREPRESENTATION_MAX_ENUM
};
struct TextureFormat
{
InternalFormatRepresentation internalFormatRepresentation = FORMATREPRESENTATION_FLOAT;
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;
};
class Vulkan
{
public:
static void shaderSwitch();
static uint32_t getNumShaderSwitches();
static void resetShaderSwitches();
static void setVsync(int vsync);
static int getVsync();
static uint32_t getSupportedVulkanApiVersion(uint32_t suggested);
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 VkImageType getImageType(TextureType);
static VkImageViewType getImageViewType(TextureType);
static VkPolygonMode getPolygonMode(bool wireframe);
static VkFilter getFilter(SamplerState::FilterMode);
static VkSamplerAddressMode getWrapMode(SamplerState::WrapMode);
static VkCompareOp getCompareOp(CompareMode);
static VkSamplerMipmapMode getMipMapMode(SamplerState::MipmapFilterMode);
static VkDescriptorType getDescriptorType(graphics::Shader::UniformType);
static VkStencilOp getStencilOp(StencilAction);
static VkIndexType getVulkanIndexBufferType(IndexDataType type);
static void cmdTransitionImageLayout(
VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout,
uint32_t baseLevel = 0, uint32_t levelCount = VK_REMAINING_MIP_LEVELS, uint32_t baseLayer = 0, uint32_t layerCount = VK_REMAINING_ARRAY_LAYERS);
};
} // vulkan
} // graphics
} // love
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
#ifndef VK_MAKE_API_VERSION
#define VK_MAKE_API_VERSION(variant, major, minor, patch) \
((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
#endif
#ifndef VK_API_VERSION_MAJOR
#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU)
#endif
#ifndef VK_API_VERSION_MINOR
#define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU)
#endif
#ifndef VK_API_VERSION_PATCH
#define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU)
#endif
#include "libraries/volk/volk.h"
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#include "libraries/vma/vk_mem_alloc.h"
+32 -20
View File
@@ -21,6 +21,10 @@
// LOVE
#include "common/config.h"
#include "graphics/Graphics.h"
#ifdef LOVE_GRAPHICS_VULKAN
# include "graphics/vulkan/Graphics.h"
# include "graphics/vulkan/Vulkan.h"
#endif
#include "Window.h"
#ifdef LOVE_ANDROID
@@ -362,25 +366,27 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
return false;
}
if (attribs != nullptr)
{
glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
contexterror = std::string(SDL_GetError());
// Make sure the context's version is at least what we requested.
if (glcontext && !checkGLVersion(*attribs, glversion))
if (renderer == love::graphics::Renderer::RENDERER_OPENGL) {
if (attribs != nullptr)
{
SDL_GL_DeleteContext(glcontext);
glcontext = nullptr;
}
glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
{
SDL_DestroyWindow(window);
window = nullptr;
return false;
if (!glcontext)
contexterror = std::string(SDL_GetError());
// Make sure the context's version is at least what we requested.
if (glcontext && !checkGLVersion(*attribs, glversion))
{
SDL_GL_DeleteContext(glcontext);
glcontext = nullptr;
}
if (!glcontext)
{
SDL_DestroyWindow(window);
window = nullptr;
return false;
}
}
}
@@ -596,16 +602,18 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
{
if (renderer == graphics::RENDERER_OPENGL)
sdlflags |= SDL_WINDOW_OPENGL;
#ifdef LOVE_GRAPHICS_METAL
if (renderer == graphics::RENDERER_METAL)
sdlflags |= SDL_WINDOW_METAL;
#endif
if (f.resizable)
if (renderer == graphics::RENDERER_VULKAN)
sdlflags |= SDL_WINDOW_VULKAN;
if (f.resizable)
sdlflags |= SDL_WINDOW_RESIZABLE;
if (f.borderless)
if (f.borderless)
sdlflags |= SDL_WINDOW_BORDERLESS;
// Note: this flag is ignored on Windows.
@@ -1110,6 +1118,10 @@ void Window::setVSync(int vsync)
SDL_GL_SetSwapInterval(1);
}
#ifdef LOVE_GRAPHICS_VULKAN
love::graphics::vulkan::Vulkan::setVsync(vsync);
#endif
#if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS)
if (metalView != nullptr)
{