mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
Merge pull request #1789 from nikeinikei/vulkan
Vulkan Graphics Backend
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -71,6 +71,7 @@ enum Renderer
|
||||
RENDERER_NONE,
|
||||
RENDERER_OPENGL,
|
||||
RENDERER_METAL,
|
||||
RENDERER_VULKAN,
|
||||
RENDERER_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -59,6 +59,7 @@ enum BufferUsage
|
||||
BUFFERUSAGE_VERTEX = 0,
|
||||
BUFFERUSAGE_INDEX,
|
||||
BUFFERUSAGE_TEXEL,
|
||||
BUFFERUSAGE_UNIFORM,
|
||||
BUFFERUSAGE_SHADER_STORAGE,
|
||||
BUFFERUSAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
®ion
|
||||
);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
|
||||
level, 1, baseLayer, 1);
|
||||
}
|
||||
else
|
||||
vkCmdCopyBufferToImage(
|
||||
commandBuffer,
|
||||
stagingBuffer,
|
||||
textureImage,
|
||||
imageLayout,
|
||||
1,
|
||||
®ion
|
||||
);
|
||||
|
||||
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, ®ion);
|
||||
|
||||
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, ®ion);
|
||||
}
|
||||
|
||||
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, ®ion);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout);
|
||||
}
|
||||
else
|
||||
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion);
|
||||
}
|
||||
|
||||
} // vulkan
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user