Merge pull request #1789 from nikeinikei/vulkan

Vulkan Graphics Backend
This commit is contained in:
slime73
2022-09-19 16:44:06 -03:00
committed by GitHub
33 changed files with 31166 additions and 61 deletions
+8 -1
View File
@@ -15,7 +15,8 @@ jobs:
libxfixes-dev libxi-dev libxinerama-dev libxxf86vm-dev libxss-dev \
libgl1-mesa-dev libdbus-1-dev libudev-dev libgles2-mesa-dev \
libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev \
libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev
libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev \
libvulkan-dev
- name: Checkout love-appimage-source
uses: actions/checkout@v2
with:
@@ -60,6 +61,12 @@ jobs:
uses: actions/checkout@v2
with:
path: megasource/libs/love
- name: Prepare Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.204.0
vulkan-components: Vulkan-Headers, Vulkan-Loader
vulkan-use-cache: true
- name: Configure
shell: cmd
env:
+1
View File
@@ -67,3 +67,4 @@ libtool
stamp-h1
/src/love
/src/tags
.vscode/
+89 -4
View File
@@ -75,6 +75,8 @@ if(POLICY CMP0072)
endif()
if(MEGA)
find_package(Vulkan REQUIRED)
# LOVE_MSVC_DLLS contains runtime DLLs that should be bundled with the love
# binary (in e.g. the installer). Example: msvcp140.dll.
set(LOVE_MSVC_DLLS ${MEGA_MSVC_DLLS})
@@ -82,7 +84,7 @@ if(MEGA)
# LOVE_INCLUDE_DIRS contains the search directories for #include. It's mostly
# not needed for MEGA builds, since almost all the libraries (except LuaJIT)
# are CMake targets, causing include paths to be added automatically.
set(LOVE_INCLUDE_DIRS)
set(LOVE_INCLUDE_DIRS ${Vulkan_INCLUDE_DIRS})
if(APPLE)
# Some files do #include <SDL2/SDL.h>, but building with megasource
@@ -154,7 +156,7 @@ if(MEGA)
# automatically.
endif()
else()
if(MSVC or ANDROID)
if(MSVC OR ANDROID)
message(FATAL_ERROR "
It is currently only possible to build with megasource on Windows and Android.
Please see https://github.com/love2d/megasource
@@ -170,6 +172,7 @@ Please see https://github.com/love2d/megasource
find_package(Vorbis REQUIRED)
find_package(ZLIB REQUIRED)
find_package(Ogg REQUIRED)
find_package(Vulkan REQUIRED)
# required for enet
add_definitions(-D HAS_SOCKLEN_T)
@@ -183,6 +186,7 @@ Please see https://github.com/love2d/megasource
${MODPLUG_INCLUDE_DIR}
${OGG_INCLUDE_DIR}
${THEORA_INCLUDE_DIR}
${Vulkan_INCLUDE_DIRS}
)
set(LOVE_LINK_LIBRARIES
@@ -590,13 +594,34 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
src/modules/graphics/opengl/Texture.h
)
set(LOVE_SRC_MODULE_GRAPHICS_VULKAN
src/modules/graphics/vulkan/Graphics.h
src/modules/graphics/vulkan/Graphics.cpp
src/modules/graphics/vulkan/GraphicsReadback.h
src/modules/graphics/vulkan/GraphicsReadback.cpp
src/modules/graphics/vulkan/Shader.h
src/modules/graphics/vulkan/Shader.cpp
src/modules/graphics/vulkan/ShaderStage.h
src/modules/graphics/vulkan/ShaderStage.cpp
src/modules/graphics/vulkan/StreamBuffer.h
src/modules/graphics/vulkan/StreamBuffer.cpp
src/modules/graphics/vulkan/Buffer.h
src/modules/graphics/vulkan/Buffer.cpp
src/modules/graphics/vulkan/Texture.h
src/modules/graphics/vulkan/Texture.cpp
src/modules/graphics/vulkan/Vulkan.h
src/modules/graphics/vulkan/Vulkan.cpp
src/modules/graphics/vulkan/VulkanWrapper.h)
set(LOVE_SRC_MODULE_GRAPHICS
${LOVE_SRC_MODULE_GRAPHICS_ROOT}
${LOVE_SRC_MODULE_GRAPHICS_OPENGL}
${LOVE_SRC_MODULE_GRAPHICS_VULKAN}
)
source_group("modules\\graphics" FILES ${LOVE_SRC_MODULE_GRAPHICS_ROOT})
source_group("modules\\graphics\\opengl" FILES ${LOVE_SRC_MODULE_GRAPHICS_OPENGL})
source_group("modules\\graphics\\vulkan" FILES ${LOVE_SRC_MODULE_GRAPHICS_VULKAN})
#
# love.image
@@ -1557,12 +1582,15 @@ set(LOVE_SRC_3P_LUAHTTPS_WINDOWS
# compile on supported platforms.
set(LOVE_SRC_3P_LUAHTTPS
${LOVE_SRC_3P_LUAHTTPS_ANDROID}
${LOVE_SRC_3P_LUAHTTPS_APPLE}
${LOVE_SRC_3P_LUAHTTPS_COMMON}
${LOVE_SRC_3P_LUAHTTPS_GENERIC}
${LOVE_SRC_3P_LUAHTTPS_LUA}
${LOVE_SRC_3P_LUAHTTPS_WINDOWS}
)
)
if (APPLE)
set(LOVE_SRC_3P_LUAHTTPS ${LOVE_SRC_3P_LUAHTTPS} ${LOVE_SRC_3P_LUAHTTPS_APPLE})
endif()
set(LOVE_LINK_L3P_LUAHTTPS)
if(MSVC)
@@ -1647,6 +1675,43 @@ endif()
add_library(love_3p_physfs ${LOVE_SRC_3P_PHYSFS})
#
# spirv_cross
#
set(LOVE_SRC_3P_SPIRV_CROSS
src/libraries/spirv_cross/GLSL.std.450.h
src/libraries/spirv_cross/spirv_cfg.cpp
src/libraries/spirv_cross/spirv_cfg.hpp
src/libraries/spirv_cross/spirv_common.hpp
src/libraries/spirv_cross/spirv_cpp.cpp
src/libraries/spirv_cross/spirv_cpp.hpp
src/libraries/spirv_cross/spirv_cross_c.cpp
src/libraries/spirv_cross/spirv_cross_c.h
src/libraries/spirv_cross/spirv_cross_containers.hpp
src/libraries/spirv_cross/spirv_cross_error_handling.hpp
src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp
src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp
src/libraries/spirv_cross/spirv_cross_util.cpp
src/libraries/spirv_cross/spirv_cross_util.hpp
src/libraries/spirv_cross/spirv_cross.cpp
src/libraries/spirv_cross/spirv_cross.hpp
src/libraries/spirv_cross/spirv_glsl.cpp
src/libraries/spirv_cross/spirv_glsl.hpp
src/libraries/spirv_cross/spirv_hlsl.cpp
src/libraries/spirv_cross/spirv_hlsl.hpp
src/libraries/spirv_cross/spirv_msl.cpp
src/libraries/spirv_cross/spirv_msl.hpp
src/libraries/spirv_cross/spirv_parser.cpp
src/libraries/spirv_cross/spirv_parser.hpp
src/libraries/spirv_cross/spirv_reflect.cpp
src/libraries/spirv_cross/spirv_reflect.hpp
src/libraries/spirv_cross/spirv.h
src/libraries/spirv_cross/spirv.hpp
)
add_library(love_3p_spirv_cross ${LOVE_SRC_3P_SPIRV_CROSS})
#
# stb_image
#
@@ -1687,6 +1752,24 @@ set(LOVE_SRC_3P_UTF8
# This library is all headers ... so there is no need to
# add_library() here.
#
# vma
#
set(LOVE_SRC_3P_VMA src/libraries/vma/vk_mem_alloc.h)
# vulkan memory allocatory has no implementation files of its own.
#
# volk
#
set(LOVE_SRC_3P_VOLK
src/libraries/volk/volk.h
src/libraries/volk/volk.c)
add_library(love_3p_volk ${LOVE_SRC_3P_VOLK})
#
# Wuff
#
@@ -1728,6 +1811,8 @@ set(LOVE_3P
love_3p_lz4
love_3p_noise1234
love_3p_physfs
love_3p_spirv_cross
love_3p_volk
love_3p_wuff
love_3p_xxhash
)
+1
View File
@@ -15,6 +15,7 @@ Released: N/A
* Added Joystick:getGamepadType.
* Added new Gamepad API buttons: "misc1", "paddle1", "paddle2", "paddle3", "paddle4". and "touchpad".
* Added a Metal backend to love.graphics, available on macOS 10.15+ and iOS 13+.
* Added a Vulkan backend to love.graphics, available on Windows, Linux, and Android 7+.
* Added '--renderers a,b,c' and '--excluderenderers a,b,c' command line arguments.
* Added t.renderers and t.excluderenderers love.conf options.
* Added t.highdpi startup flag in love.conf, replacing t.window.highdpi and the highdpi flag of love.window.setMode.
+5 -3
View File
@@ -18,7 +18,7 @@ AC_C_BIGENDIAN
AC_LANG([C++])
dnl Workaround for old aclocal versions
m4_include([platform/unix/cpp11.m4])
m4_include([platform/unix/cpp14.m4])
m4_include([platform/unix/deps.m4])
includes=
@@ -26,8 +26,8 @@ includes=
AC_DEFUN([LOVE_MSG_ERROR],
[AC_MSG_ERROR([LÖVE needs "$1"[,] please install "$1" with development files and try again])])
# C++11 support in cpp11.m4
ACLOVE_CPP11_TEST
# C++14 support in cpp14.m4
ACLOVE_CPP14_TEST
# Allow people on OSX to use autotools, they need their platform files
AC_ARG_ENABLE([osx],
@@ -81,6 +81,8 @@ AS_VAR_IF([enable_module_filesystem], [yes], [enable_library_physfs=yes], [enabl
AS_VAR_IF([enable_module_physics], [yes], [enable_library_box2d=yes], [enable_library_box2d=no])
AS_VAR_IF([enable_module_image], [yes], [enable_library_ddsparse=yes], [enable_library_ddsparse=no])
AS_VAR_IF([enable_module_graphics], [yes], [enable_library_glad=yes], [enable_library_glad=no])
AS_VAR_IF([enable_module_graphics], [yes], [enable_library_spirv_cross=yes], [enable_library_spirv_cross=no])
AS_VAR_IF([enable_module_graphics], [yes], [enable_library_volk=yes], [enable_library_volk=no])
AS_VAR_IF([enable_module_graphics], [yes], [enable_library_glslang=yes], [enable_library_glslang=no])
AS_VAR_IF([enable_module_image], [yes], [enable_library_lodepng=yes], [enable_library_lodepng=no])
AS_VAR_IF([enable_module_data], [yes], [enable_library_lua53=yes], [])
@@ -22,48 +22,47 @@ AC_DEFUN([ACLOVE_GET_CLANG_VERSION], [
AC_COMPUTE_INT(aclove_clang_version_patch, __clang_patchlevel__,, aclove_clang_version_found="no")
])
AC_DEFUN([ACLOVE_CPP11_TEST_FLAG], [
aclove_cpp11_test_cxx11name="no"
ACLOVE_CXX_FLAG_TEST([-std=c++0x], aclove_cpp11_test_cxx11name="c++0x", [])
ACLOVE_CXX_FLAG_TEST([-std=c++11], aclove_cpp11_test_cxx11name="c++11", [])
AS_VAR_IF([aclove_cpp11_test_cxx11name], [no],
[AC_MSG_ERROR([LÖVE needs a C++ compiler with C++11 support])],
[CXXFLAGS="$CXXFLAGS -std=$aclove_cpp11_test_cxx11name"])
AC_DEFUN([ACLOVE_CPP14_TEST_FLAG], [
aclove_cpp14_test_cxx14name="no"
ACLOVE_CXX_FLAG_TEST([-std=c++14], aclove_cpp14_test_cxx14name="c++14", [])
AS_VAR_IF([aclove_cpp14_test_cxx14name], [no],
[AC_MSG_ERROR([LÖVE needs a C++ compiler with C++14 support])],
[CXXFLAGS="$CXXFLAGS -std=$aclove_cpp14_test_cxx14name"])
])
AC_DEFUN([ACLOVE_CPP11_CHECK_VERSION], [ dnl compiler, targetmajor, targetminor, on-failure
aclove_cpp11_check_version_status="no"
AC_DEFUN([ACLOVE_CPP14_CHECK_VERSION], [ dnl compiler, targetmajor, targetminor, on-failure
aclove_cpp14_check_version_status="no"
AC_MSG_CHECKING([whether $1 version is at least $2.$3])
AS_IF([test "$aclove_[]$1[]_version_major" -gt $2], aclove_cpp11_check_version_status="yes")
AS_IF([test "$aclove_[]$1[]_version_major" -eq $2 && test "$aclove_[]$1[]_version_minor" -ge $3], aclove_cpp11_check_version_status="yes")
AC_MSG_RESULT([$aclove_cpp11_check_version_status])
AS_VAR_IF([aclove_cpp11_check_version_status], [no],
AS_IF([test "$aclove_[]$1[]_version_major" -gt $2], aclove_cpp14_check_version_status="yes")
AS_IF([test "$aclove_[]$1[]_version_major" -eq $2 && test "$aclove_[]$1[]_version_minor" -ge $3], aclove_cpp14_check_version_status="yes")
AC_MSG_RESULT([$aclove_cpp14_check_version_status])
AS_VAR_IF([aclove_cpp14_check_version_status], [no],
[$4])
])
AC_DEFUN([ACLOVE_CPP11_TEST_VERSION_GCC], [
ACLOVE_CPP11_CHECK_VERSION([gcc], 4, 7,
[AC_MSG_ERROR([LÖVE needs a GCC version of at least 4.7])])
AC_DEFUN([ACLOVE_CPP14_TEST_VERSION_GCC], [
ACLOVE_CPP14_CHECK_VERSION([gcc], 4, 9,
[AC_MSG_ERROR([LÖVE needs a GCC version of at least 4.9])])
])
AC_DEFUN([ACLOVE_CPP11_TEST_VERSION_CLANG], [
ACLOVE_CPP11_CHECK_VERSION([clang], 3, 1,
[AC_MSG_ERROR([LÖVE needs a clang version of at least 3.1])])
AC_DEFUN([ACLOVE_CPP14_TEST_VERSION_CLANG], [
ACLOVE_CPP14_CHECK_VERSION([clang], 3, 4,
[AC_MSG_ERROR([LÖVE needs a clang version of at least 3.4])])
])
AC_DEFUN([ACLOVE_CPP11_TEST], [
ACLOVE_CPP11_TEST_FLAG
AC_DEFUN([ACLOVE_CPP14_TEST], [
ACLOVE_CPP14_TEST_FLAG
ACLOVE_GET_GCC_VERSION
ACLOVE_GET_CLANG_VERSION
# Since clang also sets gcc headers, check clang after
aclove_cpp11_test_compiler="unknown"
AS_VAR_IF([aclove_gcc_version_found], [yes], aclove_cpp11_test_compiler="gcc")
AS_VAR_IF([aclove_clang_version_found], [yes], aclove_cpp11_test_compiler="clang")
aclove_cpp14_test_compiler="unknown"
AS_VAR_IF([aclove_gcc_version_found], [yes], aclove_cpp14_test_compiler="gcc")
AS_VAR_IF([aclove_clang_version_found], [yes], aclove_cpp14_test_compiler="clang")
AS_CASE([$aclove_cpp11_test_compiler],
[gcc], [ACLOVE_CPP11_TEST_VERSION_GCC],
[clang], [ACLOVE_CPP11_TEST_VERSION_CLANG],
AS_CASE([$aclove_cpp14_test_compiler],
[gcc], [ACLOVE_CPP14_TEST_VERSION_GCC],
[clang], [ACLOVE_CPP14_TEST_VERSION_CLANG],
[AC_MSG_WARN([Could not determine compiler version])])
])
+4
View File
@@ -126,6 +126,10 @@
# define LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK
#endif
#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX) || defined(LOVE_ANDROID)
# define LOVE_GRAPHICS_VULKAN
#endif
#if defined(LOVE_MACOS) || defined(LOVE_IOS)
# define LOVE_GRAPHICS_METAL
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -109,16 +109,21 @@ namespace opengl { extern love::graphics::Graphics *createInstance(); }
#ifdef LOVE_GRAPHICS_METAL
namespace metal { extern love::graphics::Graphics *createInstance(); }
#endif
#ifdef LOVE_GRAPHICS_VULKAN
namespace vulkan { extern love::graphics::Graphics* createInstance(); }
#endif
static const Renderer rendererOrder[] = {
RENDERER_METAL,
RENDERER_OPENGL,
RENDERER_VULKAN,
};
static std::vector<Renderer> defaultRenderers =
{
RENDERER_METAL,
RENDERER_OPENGL,
RENDERER_VULKAN,
};
static std::vector<Renderer> _renderers = defaultRenderers;
@@ -148,16 +153,20 @@ Graphics *Graphics::createInstance()
{
for (auto r : rendererOrder)
{
if (std::find(_renderers.begin(), _renderers.end(), r) == _renderers.end())
continue;
#ifdef LOVE_GRAPHICS_VULKAN
if (r == RENDERER_VULKAN)
instance = vulkan::createInstance();
#endif
if (r == RENDERER_OPENGL)
instance = opengl::createInstance();
#ifdef LOVE_GRAPHICS_METAL
if (r == RENDERER_METAL)
instance = metal::createInstance();
#endif
if (instance != nullptr)
break;
}
@@ -2548,6 +2557,7 @@ STRINGMAP_CLASS_END(Graphics, Graphics::StackType, Graphics::STACK_MAX_ENUM, sta
STRINGMAP_BEGIN(Renderer, RENDERER_MAX_ENUM, renderer)
{
{ "opengl", RENDERER_OPENGL },
{ "vulkan", RENDERER_VULKAN },
{ "metal", RENDERER_METAL },
}
STRINGMAP_END(Renderer, RENDERER_MAX_ENUM, renderer)
+1
View File
@@ -71,6 +71,7 @@ enum Renderer
RENDERER_NONE,
RENDERER_OPENGL,
RENDERER_METAL,
RENDERER_VULKAN,
RENDERER_MAX_ENUM
};
+5 -4
View File
@@ -244,9 +244,9 @@ static const char vertex_header[] = R"(
static const char vertex_functions[] = R"()";
static const char vertex_main[] = R"(
attribute vec4 VertexPosition;
attribute vec4 VertexTexCoord;
attribute vec4 VertexColor;
LOVE_IO_LOCATION(0) attribute vec4 VertexPosition;
LOVE_IO_LOCATION(1) attribute vec4 VertexTexCoord;
LOVE_IO_LOCATION(2) attribute vec4 VertexColor;
varying vec4 VaryingTexCoord;
varying vec4 VaryingColor;
@@ -540,9 +540,9 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage,
std::stringstream ss;
ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n";
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
if (glsl1on3)
ss << "#define LOVE_GLSL1_ON_GLSL3 1\n";
if (isGammaCorrect())
ss << "#define LOVE_GAMMA_CORRECT 1\n";
if (info.usesMRT)
@@ -556,6 +556,7 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage,
for (const auto &def : options.defines)
ss << "#define " + def.first + " " + def.second + "\n";
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << stageinfo.uniforms;
-1
View File
@@ -61,7 +61,6 @@ public:
void readbackInternal(int slice, int mipmap, const Rect &rect, int destwidth, size_t size, void *dest);
private:
void createTexture();
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
+1
View File
@@ -59,6 +59,7 @@ enum BufferUsage
BUFFERUSAGE_VERTEX = 0,
BUFFERUSAGE_INDEX,
BUFFERUSAGE_TEXEL,
BUFFERUSAGE_UNIFORM,
BUFFERUSAGE_SHADER_STORAGE,
BUFFERUSAGE_MAX_ENUM
};
+233
View File
@@ -0,0 +1,233 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Buffer.h"
#include "Graphics.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
static VkBufferUsageFlags getUsageBit(BufferUsage mode)
{
switch (mode)
{
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
case BUFFERUSAGE_TEXEL: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
case BUFFERUSAGE_SHADER_STORAGE: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
static VkBufferUsageFlags getVulkanUsageFlags(BufferUsageFlags flags)
{
VkBufferUsageFlags vkFlags = 0;
for (int i = 0; i < BUFFERUSAGE_MAX_ENUM; i++)
{
BufferUsageFlags flag = static_cast<BufferUsageFlags>(1u << i);
if (flags & flag)
vkFlags |= getUsageBit((BufferUsage)i);
}
return vkFlags;
}
Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
: love::graphics::Buffer(gfx, settings, format, size, arraylength)
, usageFlags(settings.usageFlags)
, vgfx(dynamic_cast<Graphics*>(gfx))
, zeroInitialize(settings.zeroInitialize)
{
loadVolatile();
}
bool Buffer::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | getVulkanUsageFlags(usageFlags);
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
if (dataUsage == BUFFERDATAUSAGE_READBACK)
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
else if ((bufferInfo.usage | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) || (bufferInfo.usage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT))
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
auto result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
if (result != VK_SUCCESS)
throw love::Exception("failed to create buffer");
if (zeroInitialize)
vkCmdFillBuffer(vgfx->getCommandBufferForDataTransfer(), buffer, 0, VK_WHOLE_SIZE, 0);
if (usageFlags & BUFFERUSAGEFLAG_TEXEL)
{
VkBufferViewCreateInfo bufferViewInfo{};
bufferViewInfo.buffer = buffer;
bufferViewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
bufferViewInfo.format = Vulkan::getVulkanVertexFormat(getDataMember(0).decl.format);
bufferViewInfo.range = VK_WHOLE_SIZE;
if (vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView) != VK_SUCCESS)
throw love::Exception("failed to create texel buffer view");
}
return true;
}
void Buffer::unloadVolatile()
{
if (buffer == VK_NULL_HANDLE)
return;
auto device = vgfx->getDevice();
vgfx->queueCleanUp(
[device=device, allocator=allocator, buffer=buffer, allocation=allocation, bufferView=bufferView](){
vkDeviceWaitIdle(device);
vmaDestroyBuffer(allocator, buffer, allocation);
if (bufferView)
vkDestroyBufferView(device, bufferView, nullptr);
});
buffer = VK_NULL_HANDLE;
bufferView = VK_NULL_HANDLE;
}
Buffer::~Buffer()
{
unloadVolatile();
}
ptrdiff_t Buffer::getHandle() const
{
return (ptrdiff_t) buffer;
}
ptrdiff_t Buffer::getTexelBufferHandle() const
{
return (ptrdiff_t) bufferView;
}
void *Buffer::map(MapType map, size_t offset, size_t size)
{
if (dataUsage == BUFFERDATAUSAGE_READBACK)
{
char *data = (char*)allocInfo.pMappedData;
return (void*) (data + offset);
}
else
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &stagingAllocInfo) != VK_SUCCESS)
throw love::Exception("failed to create staging buffer");
return stagingAllocInfo.pMappedData;
}
}
bool Buffer::fill(size_t offset, size_t size, const void *data)
{
if (dataUsage == BUFFERDATAUSAGE_READBACK)
{
void *dst = (void*)((char*)allocInfo.pMappedData + offset);
memcpy(dst, data, size);
}
else
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
VkBuffer fillBuffer;
VmaAllocation fillAllocation;
VmaAllocationInfo fillAllocInfo;
if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &fillBuffer, &fillAllocation, &fillAllocInfo) != VK_SUCCESS)
throw love::Exception("failed to create fill buffer");
memcpy(fillAllocInfo.pMappedData, data, size);
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = offset;
bufferCopy.size = size;
vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), fillBuffer, buffer, 1, &bufferCopy);
vgfx->queueCleanUp([allocator = allocator, fillBuffer = fillBuffer, fillAllocation = fillAllocation]() {
vmaDestroyBuffer(allocator, fillBuffer, fillAllocation);
});
}
return true;
}
void Buffer::unmap(size_t usedoffset, size_t usedsize)
{
if (dataUsage != BUFFERDATAUSAGE_READBACK)
{
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = usedoffset;
bufferCopy.size = usedsize;
vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), stagingBuffer, buffer, 1, &bufferCopy);
vgfx->queueCleanUp([allocator = allocator, stagingBuffer = stagingBuffer, stagingAllocation = stagingAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
});
}
}
void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = sourceoffset;
bufferCopy.dstOffset = destoffset;
bufferCopy.size = size;
vkCmdCopyBuffer(commandBuffer, buffer, (VkBuffer) dest->getHandle(), 1, &bufferCopy);
}
} // vulkan
} // graphics
} // love
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Buffer.h"
#include "graphics/Volatile.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Buffer final
: public love::graphics::Buffer
, public Volatile
{
public:
Buffer(love::graphics::Graphics *gfx, const Settings& settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
virtual ~Buffer();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
void *map(MapType map, size_t offset, size_t size) override;
void unmap(size_t usedoffset, size_t usedsize) override;
bool fill(size_t offset, size_t size, const void *data) override;
void copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) override;
ptrdiff_t getHandle() const override;
ptrdiff_t getTexelBufferHandle() const override;
private:
bool zeroInitialize;
VkBuffer buffer = VK_NULL_HANDLE;
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VkBufferView bufferView = VK_NULL_HANDLE;
Graphics *vgfx = nullptr;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocation stagingAllocation;
VmaAllocationInfo allocInfo;
VmaAllocationInfo stagingAllocInfo;
BufferUsageFlags usageFlags;
};
} // vulkan
} // graphics
} // love
File diff suppressed because it is too large Load Diff
+449
View File
@@ -0,0 +1,449 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
// löve
#include "common/config.h"
#include "graphics/Graphics.h"
#include "StreamBuffer.h"
#include "ShaderStage.h"
#include "Shader.h"
#include "Texture.h"
// libraries
#include "VulkanWrapper.h"
#include "libraries/xxHash/xxhash.h"
// c++
#include <iostream>
#include <memory>
#include <functional>
#include <set>
namespace love
{
namespace graphics
{
namespace vulkan
{
struct RenderPassAttachment
{
VkFormat format = VK_FORMAT_UNDEFINED;
bool discard = true;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
bool operator==(const RenderPassAttachment &attachment) const
{
return format == attachment.format &&
discard == attachment.discard &&
msaaSamples == attachment.msaaSamples;
}
};
struct RenderPassConfiguration
{
std::vector<RenderPassAttachment> colorAttachments;
struct StaticRenderPassConfiguration
{
RenderPassAttachment depthAttachment;
bool resolve = false;
} staticData;
bool operator==(const RenderPassConfiguration &conf) const
{
return colorAttachments == conf.colorAttachments &&
(memcmp(&staticData, &conf.staticData, sizeof(StaticRenderPassConfiguration)) == 0);
}
};
struct RenderPassConfigurationHasher
{
size_t operator()(const RenderPassConfiguration &configuration) const
{
size_t hashes[] = {
XXH32(configuration.colorAttachments.data(), configuration.colorAttachments.size() * sizeof(VkFormat), 0),
XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
};
return XXH32(hashes, sizeof(hashes), 0);
}
};
struct FramebufferConfiguration
{
std::vector<VkImageView> colorViews;
struct StaticFramebufferConfiguration
{
VkImageView depthView = VK_NULL_HANDLE;
VkImageView resolveView = VK_NULL_HANDLE;
uint32_t width = 0;
uint32_t height = 0;
VkRenderPass renderPass = VK_NULL_HANDLE;
} staticData;
bool operator==(const FramebufferConfiguration &conf) const
{
return colorViews == conf.colorViews &&
(memcmp(&staticData, &conf.staticData, sizeof(StaticFramebufferConfiguration)) == 0);
}
};
struct FramebufferConfigurationHasher
{
size_t operator()(const FramebufferConfiguration &configuration) const
{
size_t hashes[] = {
XXH32(configuration.colorViews.data(), configuration.colorViews.size() * sizeof(VkImageView), 0),
XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
};
return XXH32(hashes, sizeof(hashes), 0);
}
};
struct OptionalInstanceExtensions
{
bool physicalDeviceProperties2 = false;
};
struct OptionalDeviceFeatures
{
// VK_EXT_extended_dynamic_state
bool extendedDynamicState = false;
// VK_KHR_get_memory_requirements2
bool memoryRequirements2 = false;
// VK_KHR_dedicated_allocation
bool dedicatedAllocation = false;
// VK_KHR_buffer_device_address
bool bufferDeviceAddress = false;
// VK_EXT_memory_budget
bool memoryBudget = false;
// VK_KHR_shader_float_controls
bool shaderFloatControls = false;
// VK_KHR_spirv_1_4
bool spirv14 = false;
};
struct GraphicsPipelineConfiguration
{
VkRenderPass renderPass;
VertexAttributes vertexAttributes;
Shader *shader = nullptr;
bool wireFrame;
BlendState blendState;
ColorChannelMask colorChannelMask;
VkSampleCountFlagBits msaaSamples;
uint32_t numColorAttachments;
PrimitiveType primitiveType;
struct DynamicState
{
CullMode cullmode = CULL_NONE;
Winding winding = WINDING_MAX_ENUM;
StencilAction stencilAction = STENCIL_MAX_ENUM;
CompareMode stencilCompare = COMPARE_MAX_ENUM;
DepthState depthState{};
} dynamicState;
GraphicsPipelineConfiguration()
{
memset(this, 0, sizeof(GraphicsPipelineConfiguration));
}
bool operator==(const GraphicsPipelineConfiguration &other) const
{
return memcmp(this, &other, sizeof(GraphicsPipelineConfiguration)) == 0;
}
};
struct GraphicsPipelineConfigurationHasher
{
size_t operator() (const GraphicsPipelineConfiguration &configuration) const
{
return XXH32(&configuration, sizeof(GraphicsPipelineConfiguration), 0);
}
};
struct BatchedDrawBuffers
{
StreamBuffer *vertexBuffer1;
StreamBuffer *vertexBuffer2;
StreamBuffer *indexBuffer;
StreamBuffer *constantColorBuffer;
~BatchedDrawBuffers()
{
delete vertexBuffer1;
delete vertexBuffer2;
delete indexBuffer;
delete constantColorBuffer;
}
};
struct QueueFamilyIndices
{
Optional<uint32_t> graphicsFamily;
Optional<uint32_t> presentFamily;
bool isComplete() const
{
return graphicsFamily.hasValue && presentFamily.hasValue;
}
};
struct SwapChainSupportDetails
{
VkSurfaceCapabilitiesKHR capabilities{};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
struct RenderpassState
{
bool active = false;
VkRenderPassBeginInfo beginInfo{};
bool useConfigurations = false;
RenderPassConfiguration renderPassConfiguration{};
FramebufferConfiguration framebufferConfiguration{};
VkPipeline pipeline = VK_NULL_HANDLE;
std::vector<VkImage> transitionImages;
uint32_t numColorAttachments = 0;
float width = 0.0f;
float height = 0.0f;
VkSampleCountFlagBits msaa = VK_SAMPLE_COUNT_1_BIT;
};
struct ScreenshotReadbackBuffer
{
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
VkImage image;
VmaAllocation imageAllocation;
};
class Graphics final : public love::graphics::Graphics
{
public:
Graphics();
~Graphics();
const char *getName() const override;
const VkDevice getDevice() const;
const VmaAllocator getVmaAllocator() const;
// implementation for virtual functions
love::graphics::Texture *newTexture(const love::graphics::Texture::Settings &settings, const love::graphics::Texture::Slices *data) override;
love::graphics::Buffer *newBuffer(const love::graphics::Buffer::Settings &settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void *data, size_t size, size_t arraylength) override;
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override;
void present(void *screenshotCallbackdata) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
int getRequestedBackbufferMSAA() const override;
int getBackbufferMSAA() const override;
void setColor(Colorf c) override;
void setScissor(const Rect &rect) override;
void setScissor() override;
void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override;
void setDepthMode(CompareMode compare, bool write) override;
void setFrontFaceWinding(Winding winding) override;
void setColorMask(ColorChannelMask mask) override;
void setBlendState(const BlendState &blend) override;
void setPointSize(float size) override;
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
void draw(const DrawCommand &cmd) override;
void draw(const DrawIndexedCommand &cmd) override;
void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, graphics::Texture *texture) override;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset) override;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty) override;
// internal functions.
VkCommandBuffer getCommandBufferForDataTransfer();
void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(std::function<void()> callback);
void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr);
uint32_t getNumImagesInFlight() const;
uint32_t getFrameIndex() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
graphics::Texture *getDefaultTexture() const;
VkSampler getCachedSampler(const SamplerState &sampler);
void setComputeShader(Shader *computeShader);
std::set<Shader*> &getUsedShadersInFrame();
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
const OptionalDeviceFeatures &getEnabledOptionalDeviceExtensions() const;
VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
protected:
graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
graphics::Shader *newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
graphics::StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override;
void initCapabilities() override;
void getAPIStats(int &shaderswitches) const override;
void setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
private:
void createVulkanInstance();
bool checkValidationSupport();
void pickPhysicalDevice();
int rateDeviceSuitability(VkPhysicalDevice device);
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
void createLogicalDevice();
void initVMA();
void createSurface();
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities);
VkCompositeAlphaFlagBitsKHR chooseCompositeAlpha(const VkSurfaceCapabilitiesKHR &capabilities);
void createSwapChain();
void createImageViews();
void createScreenshotCallbackBuffers();
void createDefaultRenderPass();
void createDefaultFramebuffers();
VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration);
VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration);
void createDefaultShaders();
VkRenderPass createRenderPass(RenderPassConfiguration &configuration);
VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration &configuration);
void createColorResources();
VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
VkFormat findDepthFormat();
void createDepthResources();
void createCommandPool();
void createCommandBuffers();
void createSyncObjects();
void createDefaultTexture();
void cleanup();
void cleanupSwapChain();
void recreateSwapChain();
void initDynamicState();
void beginFrame();
void startRecordingGraphicsCommands(bool newFrame);
void endRecordingGraphicsCommands(bool present);
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration &configuration);
void updatedBatchedDrawBuffers();
bool usesConstantVertexColor(const VertexAttributes &attribs);
void createVulkanVertexFormat(
VertexAttributes vertexAttributes,
std::vector<VkVertexInputBindingDescription> &bindingDescriptions,
std::vector<VkVertexInputAttributeDescription> &attributeDescriptions);
void prepareDraw(
const VertexAttributes &attributes,
const BufferBindings &buffers, graphics::Texture *texture,
PrimitiveType, CullMode);
void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture);
void setDefaultRenderPass();
void startRenderPass();
void endRenderPass();
VkSampler createSampler(const SamplerState &sampler);
void cleanupUnusedObjects();
uint32_t vulkanApiVersion = VK_VERSION_1_0;
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
bool windowHasStencil = false;
int requestedMsaa = 0;
VkDevice device = VK_NULL_HANDLE;
OptionalInstanceExtensions optionalInstanceExtensions;
OptionalDeviceFeatures optionalDeviceFeatures;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
VkSurfaceTransformFlagBitsKHR preTransform = {};
Matrix4 displayRotation;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
VkExtent2D swapChainExtent = VkExtent2D();
std::vector<VkImageView> swapChainImageViews;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
VkImage colorImage = VK_NULL_HANDLE;
VkImageView colorImageView = VK_NULL_HANDLE;
VmaAllocation colorImageAllocation = VK_NULL_HANDLE;
VkImage depthImage = VK_NULL_HANDLE;
VkImageView depthImageView = VK_NULL_HANDLE;
VmaAllocation depthImageAllocation = VK_NULL_HANDLE;
VkRenderPass defaultRenderPass = VK_NULL_HANDLE;
std::vector<VkFramebuffer> defaultFramebuffers;
std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers;
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
std::unordered_map<VkRenderPass, bool> renderPassUsages;
std::unordered_map<VkFramebuffer, bool> framebufferUsages;
std::unordered_map<VkPipeline, bool> pipelineUsages;
std::unordered_map<uint64, VkSampler> samplers;
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
Shader* computeShader = nullptr;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment = 0;
bool imageRequested = false;
uint32_t frameCounter = 0;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
bool transitionColorDepthLayouts = false;
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
std::unique_ptr<Texture> standardTexture = nullptr;
// we need an array of draw buffers, since the frames are being rendered asynchronously
// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
std::vector<BatchedDrawBuffers> batchedDrawBuffers;
// functions that need to be called to cleanup objects that were needed for rendering a frame.
// just like batchedDrawBuffers we need a vector for each frame in flight.
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
std::vector<ScreenshotReadbackBuffer> screenshotReadbackBuffers;
std::set<Shader*> usedShadersInFrame;
RenderpassState renderPassState;
};
} // vulkan
} // graphics
} // love
@@ -0,0 +1,101 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "GraphicsReadback.h"
#include "Buffer.h"
#include "Texture.h"
#include "Graphics.h"
#include "data/ByteData.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)
: graphics::GraphicsReadback(gfx, method, buffer, offset, size, dest, destoffset)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
// Immediate readback of readback-type buffers doesn't need a staging buffer.
if (method != READBACK_IMMEDIATE || buffer->getDataUsage() != BUFFERDATAUSAGE_READBACK)
{
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
gfx->copyBuffer(buffer, stagingBuffer, offset, 0, size);
}
if (method == READBACK_IMMEDIATE)
{
vgfx->submitGpuCommands(false);
if (stagingBuffer.get()) {
status = readbackBuffer(stagingBuffer, 0, size);
gfx->releaseTemporaryBuffer(stagingBuffer);
}
else
status = readbackBuffer(buffer, offset, size);
}
else
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
}
GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty)
: graphics::GraphicsReadback(gfx, method, texture, slice, mipmap, rect, dest, destx, desty)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
size_t size = getPixelFormatSliceSize(textureFormat, rect.w, rect.h);
stagingBuffer = vgfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
vgfx->copyTextureToBuffer(texture, stagingBuffer, slice, mipmap, rect, 0, 0);
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
if (method == READBACK_IMMEDIATE)
vgfx->submitGpuCommands(false);
}
GraphicsReadback::~GraphicsReadback()
{
}
void GraphicsReadback::wait()
{
if (status == STATUS_WAITING)
vgfx->submitGpuCommands(false);
}
void GraphicsReadback::update()
{
}
} // vulkan
} // graphics
} // love
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/GraphicsReadback.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class GraphicsReadback final : public graphics::GraphicsReadback
{
public:
GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty);
virtual ~GraphicsReadback();
void wait() override;
void update() override;
private:
Graphics *vgfx = nullptr;
StrongRef<love::graphics::Buffer> stagingBuffer;
};
} // vulkan
} // graphics
} // love
File diff suppressed because it is too large Load Diff
+153
View File
@@ -0,0 +1,153 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
// LÖVE
#include "common/Optional.h"
#include "graphics/Shader.h"
#include "graphics/vulkan/ShaderStage.h"
#include "Vulkan.h"
// Libraries
#include "VulkanWrapper.h"
#include "libraries/spirv_cross/spirv_reflect.hpp"
// C++
#include <map>
#include <memory>
#include <unordered_map>
#include <queue>
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Shader final
: public graphics::Shader
, public Volatile
{
public:
Shader(StrongRef<love::graphics::ShaderStage> stages[]);
virtual ~Shader();
bool loadVolatile() override;
void unloadVolatile() override;
VkPipeline getComputePipeline() const;
const std::vector<VkPipelineShaderStageCreateInfo> &getShaderStages() const;
const VkPipelineLayout getGraphicsPipelineLayout() const;
void newFrame(uint32_t frameIndex);
void cmdPushDescriptorSets(VkCommandBuffer, VkPipelineBindPoint);
void attach() override;
ptrdiff_t getHandle() const { return 0; }
std::string getWarnings() const override { return ""; }
int getVertexAttributeIndex(const std::string &name) override;
const UniformInfo *getUniformInfo(const std::string &name) const override;
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
void updateUniform(const UniformInfo *info, int count) override;
void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
bool hasUniform(const std::string &name) const override;
void setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) override;
void setMainTex(graphics::Texture *texture);
private:
void calculateUniformBufferSizeAligned();
void compileShaders();
void createDescriptorSetLayout();
void createPipelineLayout();
void createDescriptorPoolSizes();
void createStreamBuffers();
void buildLocalUniforms(
spirv_cross::Compiler &comp,
const spirv_cross::SPIRType &type,
size_t baseoff,
const std::string &basename);
void initDescriptorSet();
void updateUniform(const UniformInfo* info, int count, bool internal);
VkDescriptorSet allocateDescriptorSet();
VkDeviceSize uniformBufferSizeAligned;
VkPipeline computePipeline;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
// we don't know how much memory we need per frame for the uniform buffer descriptors
// we keep a vector of stream buffers per frame in flight
// that gets dynamically increased if more memory is needed
std::vector<std::vector<StreamBuffer*>> streamBuffers;
std::vector<VkDescriptorPool> descriptorPools;
std::queue<VkDescriptorSet> freeDescriptorSets;
std::vector<std::vector<VkDescriptorSet>> descriptorSetsVector;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> shaderModules;
Graphics *vgfx = nullptr;
VkDevice device;
bool isCompute = false;
std::unordered_map<std::string, graphics::Shader::UniformInfo> uniformInfos;
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
std::vector<uint8> localUniformData;
std::vector<uint8> localUniformStagingData;
uint32_t uniformLocation;
OptionalInt builtinUniformDataOffset;
std::unordered_map<std::string, int> attributes;
VkDescriptorSet currentDescriptorSet;
uint32_t currentFrame;
uint32_t currentUsedUniformStreamBuffersCount;
uint32_t currentUsedDescriptorSetsCount;
};
}
}
}
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "ShaderStage.h"
#include "Graphics.h"
#include "libraries/glslang/glslang/Public/ShaderLang.h"
#include "libraries/glslang/SPIRV/GlslangToSpv.h"
#include <fstream>
#include <cstdio>
namespace love
{
namespace graphics
{
namespace vulkan
{
ShaderStage::ShaderStage(love::graphics::Graphics *gfx, ShaderStageType stage, const std::string &glsl, bool gles, const std::string &cachekey)
: love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey)
{
// the compilation is done in Shader.
}
ptrdiff_t ShaderStage::getHandle() const
{
return 0;
}
} // love
} // graphics
} // vulkan
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/ShaderStage.h"
#include "modules/graphics/Graphics.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class ShaderStage final : public graphics::ShaderStage
{
public:
ShaderStage(love::graphics::Graphics *gfx, ShaderStageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
ptrdiff_t getHandle() const override;
};
}
}
}
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "StreamBuffer.h"
#include "Graphics.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
static VkBufferUsageFlags getUsageFlags(BufferUsage mode)
{
switch (mode)
{
case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
default:
throw love::Exception("unsupported BufferUsage mode");
}
}
StreamBuffer::StreamBuffer(graphics::Graphics *gfx, BufferUsage mode, size_t size)
: love::graphics::StreamBuffer(mode, size)
, vgfx(dynamic_cast<Graphics*>(gfx))
{
loadVolatile();
}
bool StreamBuffer::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize();
bufferInfo.usage = getUsageFlags(mode);
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
usedGPUMemory = 0;
return true;
}
void StreamBuffer::unloadVolatile()
{
if (buffer == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){
vmaDestroyBuffer(allocator, buffer, allocation);
});
buffer = VK_NULL_HANDLE;
}
StreamBuffer::~StreamBuffer()
{
unloadVolatile();
}
ptrdiff_t StreamBuffer::getHandle() const
{
return (ptrdiff_t) buffer;
}
love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize)
{
(void)minsize;
return love::graphics::StreamBuffer::MapInfo((uint8*) allocInfo.pMappedData + usedGPUMemory, getSize());
}
size_t StreamBuffer::unmap(size_t usedSize)
{
return usedGPUMemory;
}
void StreamBuffer::markUsed(size_t usedSize)
{
usedGPUMemory += usedSize;
}
void StreamBuffer::nextFrame()
{
usedGPUMemory = 0;
}
} // vulkan
} // graphics
} // love
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Volatile.h"
#include "graphics/StreamBuffer.h"
#include "graphics/Graphics.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class StreamBuffer final
: public love::graphics::StreamBuffer
, public graphics::Volatile
{
public:
StreamBuffer(graphics::Graphics *gfx, BufferUsage mode, size_t size);
virtual ~StreamBuffer();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
MapInfo map(size_t minsize) override;
size_t unmap(size_t usedSize) override;
void markUsed(size_t usedSize) override;
void nextFrame() override;
ptrdiff_t getHandle() const override;
private:
Graphics *vgfx = nullptr;
VmaAllocator allocator;
VmaAllocation allocation;
VmaAllocationInfo allocInfo;
VkBuffer buffer = VK_NULL_HANDLE;
size_t usedGPUMemory;
};
} // vulkan
} // graphics
} // love
+589
View File
@@ -0,0 +1,589 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Texture.h"
#include "Graphics.h"
#include "Vulkan.h"
#include <limits>
namespace love
{
namespace graphics
{
namespace vulkan
{
Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data)
: love::graphics::Texture(gfx, settings, data)
, vgfx(dynamic_cast<Graphics*>(gfx))
, slices(settings.type)
, imageAspect(0)
{
if (data)
slices = *data;
loadVolatile();
}
bool Texture::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
if (isPixelFormatDepthStencil(format))
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
else if (isPixelFormatDepth(format))
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
else
imageAspect |= VK_IMAGE_ASPECT_COLOR_BIT;
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageUsageFlags usageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (readable)
{
if (!isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
if (!isPixelFormatCompressed(format) && !isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
}
if (renderTarget)
{
if (isPixelFormatDepthStencil(format))
usageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
else
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
VkImageCreateFlags createFlags = 0;
layerCount = 1;
if (texType == TEXTURE_2D_ARRAY)
layerCount = getLayerCount();
else if (texType == TEXTURE_CUBE)
{
layerCount = 6;
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = createFlags;
imageInfo.imageType = Vulkan::getImageType(getTextureType());
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
imageInfo.extent.depth = static_cast<uint32_t>(depth);
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
imageInfo.format = vulkanFormat.internalFormat;
if (isPixelFormatCompressed(format))
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
else
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = msaaSamples;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
throw love::Exception("failed to create image");
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (isPixelFormatDepthStencil(format))
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
else if (computeWrite)
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
else
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
bool hasdata = slices.get(0, 0) != nullptr;
if (hasdata)
for (int mip = 0; mip < getMipmapCount(); mip++)
{
int sliceCount;
if (texType == TEXTURE_CUBE)
sliceCount = 6;
else
sliceCount = slices.getSliceCount();
for (int slice = 0; slice < sliceCount; slice++)
{
auto id = slices.get(slice, mip);
if (id != nullptr)
uploadImageData(id, mip, slice, 0, 0);
}
}
else
clear();
createTextureImageView();
textureSampler = vgfx->getCachedSampler(samplerState);
if (!isPixelFormatDepthStencil(format) && mipmapCount > 1 && getMipmapsMode() != MIPMAPS_NONE)
generateMipmaps();
if (renderTarget)
{
renderTargetImageViews.resize(getMipmapCount());
for (int mip = 0; mip < getMipmapCount(); mip++)
{
renderTargetImageViews.at(mip).resize(layerCount);
for (int slice = 0; slice < layerCount; slice++)
{
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = mip;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = slice;
viewInfo.subresourceRange.layerCount = 1;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
viewInfo.components.b = vulkanFormat.swizzleB;
viewInfo.components.a = vulkanFormat.swizzleA;
if (vkCreateImageView(device, &viewInfo, nullptr, &renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS)
throw love::Exception("could not create render target image view");
}
}
}
return true;
}
void Texture::unloadVolatile()
{
if (textureImage == VK_NULL_HANDLE)
return;
vgfx->queueCleanUp([
device = device,
textureImageView = textureImageView,
allocator = allocator,
textureImage = textureImage,
textureImageAllocation = textureImageAllocation,
textureImageViews = std::move(renderTargetImageViews)] () {
vkDestroyImageView(device, textureImageView, nullptr);
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
for (const auto &views : textureImageViews)
for (const auto &view : views)
vkDestroyImageView(device, view, nullptr);
});
textureImage = VK_NULL_HANDLE;
}
Texture::~Texture()
{
unloadVolatile();
}
ptrdiff_t Texture::getRenderTargetHandle() const
{
return (ptrdiff_t)textureImageView;
}
ptrdiff_t Texture::getSamplerHandle() const
{
return (ptrdiff_t)textureSampler;
}
VkImageView Texture::getRenderTargetView(int mip, int layer)
{
return renderTargetImageViews.at(mip).at(layer);
}
VkSampleCountFlagBits Texture::getMsaaSamples() const
{
return msaaSamples;
}
int Texture::getMSAA() const
{
return static_cast<int>(msaaSamples);
}
ptrdiff_t Texture::getHandle() const
{
return (ptrdiff_t)textureImage;
}
void Texture::setSamplerState(const SamplerState &s)
{
love::graphics::Texture::setSamplerState(s);
textureSampler = vgfx->getCachedSampler(s);
}
VkImageLayout Texture::getImageLayout() const
{
return imageLayout;
}
void Texture::createTextureImageView()
{
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = getMipmapCount();
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = layerCount;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
viewInfo.components.b = vulkanFormat.swizzleB;
viewInfo.components.a = vulkanFormat.swizzleA;
if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS)
throw love::Exception("could not create texture image view");
}
void Texture::clear()
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceRange range{};
range.aspectMask = imageAspect;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
if (imageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
auto clearColor = getClearValue();
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
}
else if (imageLayout == VK_IMAGE_LAYOUT_GENERAL)
{
auto clearColor = getClearValue();
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range);
}
else
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
VkClearDepthStencilValue depthStencilColor{};
depthStencilColor.depth = 0.0f;
depthStencilColor.stencil = 0;
vkCmdClearDepthStencilImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &depthStencilColor, 1, &range);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
}
}
VkClearColorValue Texture::getClearValue()
{
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkClearColorValue clearColor{};
switch (vulkanFormat.internalFormatRepresentation)
{
case FORMATREPRESENTATION_FLOAT:
clearColor.float32[0] = 0.0f;
clearColor.float32[1] = 0.0f;
clearColor.float32[2] = 0.0f;
clearColor.float32[3] = 0.0f;
break;
case FORMATREPRESENTATION_SINT:
clearColor.int32[0] = 0;
clearColor.int32[1] = 0;
clearColor.int32[2] = 0;
clearColor.int32[3] = 0;
break;
case FORMATREPRESENTATION_UINT:
clearColor.uint32[0] = 0;
clearColor.uint32[1] = 0;
clearColor.uint32[2] = 0;
clearColor.uint32[3] = 0;
break;
}
return clearColor;
}
void Texture::generateMipmapsInternal()
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, static_cast<uint32_t>(getMipmapCount()), 0, static_cast<uint32_t>(layerCount));
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = textureImage;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = static_cast<uint32_t>(layerCount);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1u;
uint32_t mipLevels = static_cast<uint32_t>(getMipmapCount());
for (uint32_t i = 1; i < mipLevels; i++)
{
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { getPixelWidth(i - 1), getPixelHeight(i - 1), 1 };
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = static_cast<uint32_t>(layerCount);
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { getPixelWidth(i), getPixelHeight(i), 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = static_cast<uint32_t>(layerCount);
vkCmdBlitImage(commandBuffer,
textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &blit,
VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
}
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
}
void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
{
VkBuffer stagingBuffer;
VmaAllocation vmaAllocation;
VkBufferCreateInfo bufferCreateInfo{};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCreateInfo.size = size;
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo);
memcpy(allocInfo.pMappedData, data, size);
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
uint32_t baseLayer;
if (getTextureType() == TEXTURE_VOLUME)
baseLayer = 0;
else
baseLayer = slice;
region.imageSubresource.aspectMask = imageAspect;
region.imageSubresource.mipLevel = level;
region.imageSubresource.baseArrayLayer = baseLayer;
region.imageSubresource.layerCount = 1;
region.imageOffset = { r.x, r.y, 0 };
region.imageExtent = {
static_cast<uint32_t>(r.w),
static_cast<uint32_t>(r.h), 1
};
if (getTextureType() == TEXTURE_VOLUME)
region.imageOffset.z = slice;
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
level, 1, baseLayer, 1);
vkCmdCopyBufferToImage(
commandBuffer,
stagingBuffer,
textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
level, 1, baseLayer, 1);
}
else
vkCmdCopyBufferToImage(
commandBuffer,
stagingBuffer,
textureImage,
imageLayout,
1,
&region
);
vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() {
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
});
}
void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.layerCount = 1;
VkBufferImageCopy region{};
region.bufferOffset = sourceoffset;
region.bufferRowLength = sourcewidth;
region.bufferImageHeight = 1;
region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w);
region.imageExtent.height = static_cast<uint32_t>(rect.h);
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
else
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
}
void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size)
{
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.layerCount = 1;
VkBufferImageCopy region{};
region.bufferOffset = destoffset;
region.bufferRowLength = destwidth;
region.bufferImageHeight = 0;
region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w);
region.imageExtent.height = static_cast<uint32_t>(rect.h);
region.imageExtent.depth = 1;
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
{
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, &region);
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout);
}
else
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, &region);
}
} // vulkan
} // graphics
} // love
+92
View File
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
class Graphics;
class Texture final
: public graphics::Texture
, public Volatile
{
public:
Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data);
~Texture();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
void setSamplerState(const SamplerState &s) override;
VkImageLayout getImageLayout() const;
void copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
void copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override;
ptrdiff_t getRenderTargetHandle() const override;
ptrdiff_t getSamplerHandle() const override;
VkImageView getRenderTargetView(int mip, int layer);
VkSampleCountFlagBits getMsaaSamples() const;
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
void generateMipmapsInternal() override;
int getMSAA() const override;
ptrdiff_t getHandle() const override;
private:
void createTextureImageView();
void clear();
VkClearColorValue getClearValue();
Graphics *vgfx = nullptr;
VkDevice device = VK_NULL_HANDLE;
VkImageAspectFlags imageAspect;
VmaAllocator allocator = VK_NULL_HANDLE;
VkImage textureImage = VK_NULL_HANDLE;
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VmaAllocation textureImageAllocation = VK_NULL_HANDLE;
VkImageView textureImageView = VK_NULL_HANDLE;
std::vector<std::vector<VkImageView>> renderTargetImageViews;
VkSampler textureSampler = VK_NULL_HANDLE;
Slices slices;
int layerCount = 0;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
};
} // vulkan
} // graphics
} // love
+990
View File
@@ -0,0 +1,990 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Vulkan.h"
#include <sstream>
namespace love
{
namespace graphics
{
namespace vulkan
{
static uint32_t numShaderSwitches;
static int vsync = 1;
void Vulkan::shaderSwitch()
{
numShaderSwitches++;
}
uint32_t Vulkan::getNumShaderSwitches()
{
return numShaderSwitches;
}
void Vulkan::resetShaderSwitches()
{
numShaderSwitches = 0;
}
void Vulkan::setVsync(int value)
{
vsync = value;
}
int Vulkan::getVsync()
{
return vsync;
}
uint32_t Vulkan::getSupportedVulkanApiVersion(uint32_t suggested)
{
#ifdef VK_VERSION_1_3
if (suggested >= VK_API_VERSION_1_3)
return VK_API_VERSION_1_3;
#endif
#ifdef VK_VERSION_1_2
if (suggested >= VK_API_VERSION_1_2)
return VK_API_VERSION_1_2;
#endif
#ifdef VK_VERSION_1_1
if (suggested >= VK_API_VERSION_1_1)
return VK_API_VERSION_1_1;
#endif
return VK_API_VERSION_1_0;
}
VkFormat Vulkan::getVulkanVertexFormat(DataFormat format)
{
switch (format)
{
case DATAFORMAT_FLOAT:
return VK_FORMAT_R32_SFLOAT;
case DATAFORMAT_FLOAT_VEC2:
return VK_FORMAT_R32G32_SFLOAT;
case DATAFORMAT_FLOAT_VEC3:
return VK_FORMAT_R32G32B32_SFLOAT;
case DATAFORMAT_FLOAT_VEC4:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case DATAFORMAT_FLOAT_MAT2X2:
case DATAFORMAT_FLOAT_MAT2X3:
case DATAFORMAT_FLOAT_MAT2X4:
case DATAFORMAT_FLOAT_MAT3X2:
case DATAFORMAT_FLOAT_MAT3X3:
case DATAFORMAT_FLOAT_MAT3X4:
case DATAFORMAT_FLOAT_MAT4X2:
case DATAFORMAT_FLOAT_MAT4X3:
case DATAFORMAT_FLOAT_MAT4X4:
throw love::Exception("unimplemented data format (matnxm)");
case DATAFORMAT_INT32:
return VK_FORMAT_R32_SINT;
case DATAFORMAT_INT32_VEC2:
return VK_FORMAT_R32G32_SINT;
case DATAFORMAT_INT32_VEC3:
return VK_FORMAT_R32G32B32_SINT;
case DATAFORMAT_INT32_VEC4:
return VK_FORMAT_R32G32B32A32_SINT;
case DATAFORMAT_UINT32:
return VK_FORMAT_R32_UINT;
case DATAFORMAT_UINT32_VEC2:
return VK_FORMAT_R32G32_UINT;
case DATAFORMAT_UINT32_VEC3:
return VK_FORMAT_R32G32B32_UINT;
case DATAFORMAT_UINT32_VEC4:
return VK_FORMAT_R32G32B32A32_UINT;
case DATAFORMAT_SNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_SNORM;
case DATAFORMAT_UNORM8_VEC4:
return VK_FORMAT_R8G8B8A8_UNORM;
case DATAFORMAT_INT8_VEC4:
return VK_FORMAT_R8G8B8A8_SINT;
case DATAFORMAT_UINT8_VEC4:
return VK_FORMAT_R8G8B8A8_UINT;
case DATAFORMAT_SNORM16_VEC2:
return VK_FORMAT_R16G16_SNORM;
case DATAFORMAT_SNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_SNORM;
case DATAFORMAT_UNORM16_VEC2:
return VK_FORMAT_R16G16_UNORM;
case DATAFORMAT_UNORM16_VEC4:
return VK_FORMAT_R16G16B16A16_UNORM;
case DATAFORMAT_INT16_VEC2:
return VK_FORMAT_R16G16_SINT;
case DATAFORMAT_INT16_VEC4:
return VK_FORMAT_R16G16B16A16_SINT;
case DATAFORMAT_UINT16:
return VK_FORMAT_R16_UINT;
case DATAFORMAT_UINT16_VEC2:
return VK_FORMAT_R16G16_UINT;
case DATAFORMAT_UINT16_VEC4:
return VK_FORMAT_R16G16B16A16_UINT;
case DATAFORMAT_BOOL:
case DATAFORMAT_BOOL_VEC2:
case DATAFORMAT_BOOL_VEC3:
case DATAFORMAT_BOOL_VEC4:
throw love::Exception("unimplemented data format (bool)");
default:
throw love::Exception("unknown data format");
}
}
TextureFormat Vulkan::getTextureFormat(PixelFormat format)
{
TextureFormat textureFormat{};
switch (format)
{
case PIXELFORMAT_UNKNOWN:
throw love::Exception("unknown pixel format");
case PIXELFORMAT_NORMAL:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB;
break;
case PIXELFORMAT_HDR:
throw love::Exception("unimplemented pixel format: hdr");
case PIXELFORMAT_R8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8_UNORM;
break;
case PIXELFORMAT_R8_INT:
textureFormat.internalFormat = VK_FORMAT_R8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_R16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16_UNORM;
break;
case PIXELFORMAT_R16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16_SFLOAT;
break;
case PIXELFORMAT_R16_INT:
textureFormat.internalFormat = VK_FORMAT_R16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_R32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32_SFLOAT;
break;
case PIXELFORMAT_R32_INT:
textureFormat.internalFormat = VK_FORMAT_R32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_R32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RG8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM;
break;
case PIXELFORMAT_RG8_INT:
textureFormat.internalFormat = VK_FORMAT_R8G8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8G8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_LA8_UNORM: // Same as RG8: but accessed as (L: L: L: A)
textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM;
textureFormat.swizzleR = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleG = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleB = VK_COMPONENT_SWIZZLE_R;
textureFormat.swizzleA = VK_COMPONENT_SWIZZLE_G;
break;
case PIXELFORMAT_RG16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16G16_UNORM;
break;
case PIXELFORMAT_RG16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16G16_SFLOAT;
break;
case PIXELFORMAT_RG16_INT:
textureFormat.internalFormat = VK_FORMAT_R16G16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16G16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RG32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32G32_SFLOAT;
break;
case PIXELFORMAT_RG32_INT:
textureFormat.internalFormat = VK_FORMAT_R32G32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RG32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32G32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA8_UNORM:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UNORM;
break;
case PIXELFORMAT_RGBA8_UNORM_sRGB:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB;
break;
case PIXELFORMAT_BGRA8_UNORM:
textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_UNORM;
break;
case PIXELFORMAT_BGRA8_UNORM_sRGB:
textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_SRGB;
break;
case PIXELFORMAT_RGBA8_INT:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA8_UINT:
textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA16_UNORM:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UNORM;
break;
case PIXELFORMAT_RGBA16_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
break;
case PIXELFORMAT_RGBA16_INT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA16_UINT:
textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
break;
case PIXELFORMAT_RGBA32_INT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT;
break;
case PIXELFORMAT_RGBA32_UINT:
textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_RGBA4_UNORM: // LSB->MSB: [a: b: g: r]
textureFormat.internalFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
break;
case PIXELFORMAT_RGB5A1_UNORM: // LSB->MSB: [a: b: g: r]
textureFormat.internalFormat = VK_FORMAT_R5G5B5A1_UNORM_PACK16;
break;
case PIXELFORMAT_RGB565_UNORM: // LSB->MSB: [b: g: r]
textureFormat.internalFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
break;
case PIXELFORMAT_RGB10A2_UNORM: // LSB->MSB: [r: g: b: a]
case PIXELFORMAT_RG11B10_FLOAT: // LSB->MSB: [r: g: b]
throw love::Exception("unimplemented pixel format (rgb10a2, rg11b10)");
case PIXELFORMAT_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_S8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH16_UNORM:
textureFormat.internalFormat = VK_FORMAT_D16_UNORM;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH24_UNORM:
case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_D24_UNORM_S8_UINT;
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
break;
case PIXELFORMAT_DEPTH32_FLOAT:
textureFormat.internalFormat = VK_FORMAT_D32_SFLOAT;
break;
case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8:
textureFormat.internalFormat = VK_FORMAT_D32_SFLOAT_S8_UINT;
break;
case PIXELFORMAT_DXT1_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
break;
case PIXELFORMAT_DXT3_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC2_UNORM_BLOCK;
break;
case PIXELFORMAT_DXT5_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC3_UNORM_BLOCK;
break;
case PIXELFORMAT_BC4_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC4_UNORM_BLOCK;
break;
case PIXELFORMAT_BC4_SNORM:
textureFormat.internalFormat = VK_FORMAT_BC4_SNORM_BLOCK;
break;
case PIXELFORMAT_BC5_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC5_UNORM_BLOCK;
break;
case PIXELFORMAT_BC5_SNORM:
textureFormat.internalFormat = VK_FORMAT_BC5_SNORM_BLOCK;
break;
case PIXELFORMAT_BC6H_UFLOAT:
textureFormat.internalFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK;
break;
case PIXELFORMAT_BC6H_FLOAT:
textureFormat.internalFormat = VK_FORMAT_BC6H_SFLOAT_BLOCK;
break;
case PIXELFORMAT_BC7_UNORM:
textureFormat.internalFormat = VK_FORMAT_BC7_UNORM_BLOCK;
break;
case PIXELFORMAT_PVR1_RGB2_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGB4_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGBA2_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_PVR1_RGBA4_UNORM:
textureFormat.internalFormat = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
break;
case PIXELFORMAT_ETC1_UNORM:
throw love::Exception("unimplemented pixel format: etc1");
case PIXELFORMAT_ETC2_RGB_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case PIXELFORMAT_ETC2_RGBA_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
break;
case PIXELFORMAT_ETC2_RGBA1_UNORM:
textureFormat.internalFormat = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_R_UNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_R_SNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11_SNORM_BLOCK;
break;
case PIXELFORMAT_EAC_RG_UNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
break;
case PIXELFORMAT_EAC_RG_SNORM:
textureFormat.internalFormat = VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
break;
case PIXELFORMAT_ASTC_4x4:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_5x4:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_5x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_6x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_6x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_8x8:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x5:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x6:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x8:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_10x10:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_12x10:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT;
#endif
break;
case PIXELFORMAT_ASTC_12x12:
#ifdef VK_EXT_texture_compression_astc_hdr
textureFormat.internalFormat = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT;
#endif
break;
default:
throw love::Exception("unknown pixel format");
}
return textureFormat;
}
// values taken from https://pcisig.com/membership/member-companies
// as specified at https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html
std::string Vulkan::getVendorName(uint32_t vendorId)
{
switch (vendorId)
{
case 4130:
return "AMD";
case 4318:
return "Nvidia";
case 32902:
return "Intel";
case 4203:
return "Apple";
case 5140:
return "Microsoft";
case 5045:
return "ARM";
case 20803:
return "Qualcomm";
case 5348:
return "Broadcom";
default:
return "unknown";
}
}
std::string Vulkan::getVulkanApiVersion(uint32_t version)
{
std::stringstream ss;
ss << VK_API_VERSION_MAJOR(version)
<< "." << VK_API_VERSION_MINOR(version)
<< "." << VK_API_VERSION_PATCH(version);
return ss.str();
}
VkPrimitiveTopology Vulkan::getPrimitiveTypeTopology(graphics::PrimitiveType primitiveType)
{
switch (primitiveType)
{
case PRIMITIVE_POINTS:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PRIMITIVE_TRIANGLES:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PRIMITIVE_TRIANGLE_FAN:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case PRIMITIVE_TRIANGLE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
default:
throw love::Exception("unknown primitive type");
}
}
VkBlendFactor Vulkan::getBlendFactor(BlendFactor blendFactor)
{
switch (blendFactor)
{
case BLENDFACTOR_ZERO:
return VK_BLEND_FACTOR_ZERO;
case BLENDFACTOR_ONE:
return VK_BLEND_FACTOR_ONE;
case BLENDFACTOR_SRC_COLOR:
return VK_BLEND_FACTOR_SRC_COLOR;
case BLENDFACTOR_ONE_MINUS_SRC_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BLENDFACTOR_SRC_ALPHA:
return VK_BLEND_FACTOR_SRC_ALPHA;
case BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BLENDFACTOR_DST_COLOR:
return VK_BLEND_FACTOR_DST_COLOR;
case BLENDFACTOR_ONE_MINUS_DST_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BLENDFACTOR_DST_ALPHA:
return VK_BLEND_FACTOR_DST_ALPHA;
case BLENDFACTOR_ONE_MINUS_DST_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BLENDFACTOR_SRC_ALPHA_SATURATED:
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
default:
throw love::Exception("unknown blend factor");
}
}
VkBlendOp Vulkan::getBlendOp(BlendOperation op)
{
switch (op)
{
case BLENDOP_ADD:
return VK_BLEND_OP_ADD;
case BLENDOP_MAX:
return VK_BLEND_OP_MAX;
case BLENDOP_MIN:
return VK_BLEND_OP_MIN;
case BLENDOP_SUBTRACT:
return VK_BLEND_OP_SUBTRACT;
case BLENDOP_REVERSE_SUBTRACT:
return VK_BLEND_OP_REVERSE_SUBTRACT;
default:
throw love::Exception("unknown blend operation");
}
}
VkBool32 Vulkan::getBool(bool b)
{
if (b)
return VK_TRUE;
else
return VK_FALSE;
}
VkColorComponentFlags Vulkan::getColorMask(ColorChannelMask mask)
{
VkColorComponentFlags flags = 0;
if (mask.r)
flags |= VK_COLOR_COMPONENT_R_BIT;
if (mask.g)
flags |= VK_COLOR_COMPONENT_G_BIT;
if (mask.b)
flags |= VK_COLOR_COMPONENT_B_BIT;
if (mask.a)
flags |= VK_COLOR_COMPONENT_A_BIT;
return flags;
}
VkFrontFace Vulkan::getFrontFace(Winding winding)
{
switch (winding)
{
case WINDING_CW:
return VK_FRONT_FACE_CLOCKWISE;
case WINDING_CCW:
return VK_FRONT_FACE_COUNTER_CLOCKWISE;
default:
throw love::Exception("unknown winding");
}
}
VkCullModeFlags Vulkan::getCullMode(CullMode cullmode)
{
switch (cullmode)
{
case CULL_BACK:
return VK_CULL_MODE_BACK_BIT;
case CULL_FRONT:
return VK_CULL_MODE_FRONT_BIT;
case CULL_NONE:
return VK_CULL_MODE_NONE;
default:
throw love::Exception("unknown cull mode");
}
}
VkImageType Vulkan::getImageType(TextureType textureType)
{
switch (textureType)
{
case TEXTURE_2D:
case TEXTURE_2D_ARRAY:
case TEXTURE_CUBE:
return VK_IMAGE_TYPE_2D;
case TEXTURE_VOLUME:
return VK_IMAGE_TYPE_3D;
default:
throw love::Exception("unknown texture type");
}
}
VkImageViewType Vulkan::getImageViewType(TextureType textureType)
{
switch (textureType)
{
case TEXTURE_2D:
return VK_IMAGE_VIEW_TYPE_2D;
case TEXTURE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case TEXTURE_CUBE:
return VK_IMAGE_VIEW_TYPE_CUBE;
case TEXTURE_VOLUME:
return VK_IMAGE_VIEW_TYPE_3D;
default:
throw love::Exception("unknown texture type");
}
}
VkPolygonMode Vulkan::getPolygonMode(bool wireframe)
{
if (wireframe)
return VK_POLYGON_MODE_LINE;
else
return VK_POLYGON_MODE_FILL;
}
VkFilter Vulkan::getFilter(SamplerState::FilterMode mode)
{
switch (mode)
{
case SamplerState::FILTER_LINEAR:
return VK_FILTER_LINEAR;
case SamplerState::FILTER_NEAREST:
return VK_FILTER_NEAREST;
default:
throw love::Exception("unkonwn filter mode");
}
}
VkSamplerAddressMode Vulkan::getWrapMode(SamplerState::WrapMode mode)
{
switch (mode)
{
//fixme: not accounting for different clamps (how does that work in vulkan?)
case SamplerState::WRAP_CLAMP:
case SamplerState::WRAP_CLAMP_ZERO:
case SamplerState::WRAP_CLAMP_ONE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerState::WRAP_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerState::WRAP_MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
default:
throw love::Exception("unknown wrap mode");
}
}
VkCompareOp Vulkan::getCompareOp(CompareMode mode)
{
switch (mode)
{
case COMPARE_LESS:
return VK_COMPARE_OP_LESS;
case COMPARE_LEQUAL:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case COMPARE_EQUAL:
return VK_COMPARE_OP_EQUAL;
case COMPARE_GEQUAL:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case COMPARE_GREATER:
return VK_COMPARE_OP_GREATER;
case COMPARE_NOTEQUAL:
return VK_COMPARE_OP_NOT_EQUAL;
case COMPARE_ALWAYS:
return VK_COMPARE_OP_ALWAYS;
case COMPARE_NEVER:
return VK_COMPARE_OP_NEVER;
default:
throw love::Exception("unknown compare mode");
}
}
VkSamplerMipmapMode Vulkan::getMipMapMode(SamplerState::MipmapFilterMode mode)
{
switch (mode)
{
case SamplerState::MIPMAP_FILTER_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerState::MIPMAP_FILTER_NONE:
case SamplerState::MIPMAP_FILTER_LINEAR:
default:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
VkDescriptorType Vulkan::getDescriptorType(graphics::Shader::UniformType type)
{
switch (type)
{
case graphics::Shader::UniformType::UNIFORM_FLOAT:
case graphics::Shader::UniformType::UNIFORM_MATRIX:
case graphics::Shader::UniformType::UNIFORM_INT:
case graphics::Shader::UniformType::UNIFORM_UINT:
case graphics::Shader::UniformType::UNIFORM_BOOL:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case graphics::Shader::UniformType::UNIFORM_SAMPLER:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case graphics::Shader::UniformType::UNIFORM_STORAGETEXTURE:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
case graphics::Shader::UniformType::UNIFORM_TEXELBUFFER:
return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
case graphics::Shader::UniformType::UNIFORM_STORAGEBUFFER:
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
default:
throw love::Exception("unkonwn uniform type");
}
}
VkStencilOp Vulkan::getStencilOp(StencilAction action)
{
switch (action)
{
case STENCIL_KEEP:
return VK_STENCIL_OP_KEEP;
case STENCIL_ZERO:
return VK_STENCIL_OP_ZERO;
case STENCIL_REPLACE:
return VK_STENCIL_OP_REPLACE;
case STENCIL_INCREMENT:
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case STENCIL_DECREMENT:
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case STENCIL_INCREMENT_WRAP:
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case STENCIL_DECREMENT_WRAP:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
case STENCIL_INVERT:
return VK_STENCIL_OP_INVERT;
default:
throw love::Exception("unknown stencil action");
}
}
VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
{
switch (type)
{
case INDEX_UINT16: return VK_INDEX_TYPE_UINT16;
case INDEX_UINT32: return VK_INDEX_TYPE_UINT32;
default:
throw love::Exception("unknown Index Data type");
}
}
void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount)
{
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = baseLevel;
barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = baseLayer;
barrier.subresourceRange.layerCount = layerCount;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
// we use general for images that are both sampled and compute write
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_UNDEFINED)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else
throw std::invalid_argument("unsupported layout transition!");
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
}
} // vulkan
} // graphics
} // love
+92
View File
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#include "graphics/Graphics.h"
#include "VulkanWrapper.h"
namespace love
{
namespace graphics
{
namespace vulkan
{
enum InternalFormatRepresentation
{
FORMATREPRESENTATION_FLOAT,
FORMATREPRESENTATION_UINT,
FORMATREPRESENTATION_SINT,
FORMATREPRESENTATION_MAX_ENUM
};
struct TextureFormat
{
InternalFormatRepresentation internalFormatRepresentation = FORMATREPRESENTATION_FLOAT;
VkFormat internalFormat = VK_FORMAT_UNDEFINED;
VkComponentSwizzle swizzleR = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleG = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleB = VK_COMPONENT_SWIZZLE_IDENTITY;
VkComponentSwizzle swizzleA = VK_COMPONENT_SWIZZLE_IDENTITY;
};
class Vulkan
{
public:
static void shaderSwitch();
static uint32_t getNumShaderSwitches();
static void resetShaderSwitches();
static void setVsync(int vsync);
static int getVsync();
static uint32_t getSupportedVulkanApiVersion(uint32_t suggested);
static VkFormat getVulkanVertexFormat(DataFormat format);
static TextureFormat getTextureFormat(PixelFormat);
static std::string getVendorName(uint32_t vendorId);
static std::string getVulkanApiVersion(uint32_t apiVersion);
static VkPrimitiveTopology getPrimitiveTypeTopology(graphics::PrimitiveType);
static VkBlendFactor getBlendFactor(BlendFactor);
static VkBlendOp getBlendOp(BlendOperation);
static VkBool32 getBool(bool);
static VkColorComponentFlags getColorMask(ColorChannelMask);
static VkFrontFace getFrontFace(Winding);
static VkCullModeFlags getCullMode(CullMode);
static VkImageType getImageType(TextureType);
static VkImageViewType getImageViewType(TextureType);
static VkPolygonMode getPolygonMode(bool wireframe);
static VkFilter getFilter(SamplerState::FilterMode);
static VkSamplerAddressMode getWrapMode(SamplerState::WrapMode);
static VkCompareOp getCompareOp(CompareMode);
static VkSamplerMipmapMode getMipMapMode(SamplerState::MipmapFilterMode);
static VkDescriptorType getDescriptorType(graphics::Shader::UniformType);
static VkStencilOp getStencilOp(StencilAction);
static VkIndexType getVulkanIndexBufferType(IndexDataType type);
static void cmdTransitionImageLayout(
VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout,
uint32_t baseLevel = 0, uint32_t levelCount = VK_REMAINING_MIP_LEVELS, uint32_t baseLayer = 0, uint32_t layerCount = VK_REMAINING_ARRAY_LAYERS);
};
} // vulkan
} // graphics
} // love
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2006-2022 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
#ifndef VK_MAKE_API_VERSION
#define VK_MAKE_API_VERSION(variant, major, minor, patch) \
((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
#endif
#ifndef VK_API_VERSION_MAJOR
#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU)
#endif
#ifndef VK_API_VERSION_MINOR
#define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU)
#endif
#ifndef VK_API_VERSION_PATCH
#define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU)
#endif
#include "libraries/volk/volk.h"
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#include "libraries/vma/vk_mem_alloc.h"
+32 -20
View File
@@ -21,6 +21,10 @@
// LOVE
#include "common/config.h"
#include "graphics/Graphics.h"
#ifdef LOVE_GRAPHICS_VULKAN
# include "graphics/vulkan/Graphics.h"
# include "graphics/vulkan/Vulkan.h"
#endif
#include "Window.h"
#ifdef LOVE_ANDROID
@@ -362,25 +366,27 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
return false;
}
if (attribs != nullptr)
{
glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
contexterror = std::string(SDL_GetError());
// Make sure the context's version is at least what we requested.
if (glcontext && !checkGLVersion(*attribs, glversion))
if (renderer == love::graphics::Renderer::RENDERER_OPENGL) {
if (attribs != nullptr)
{
SDL_GL_DeleteContext(glcontext);
glcontext = nullptr;
}
glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
{
SDL_DestroyWindow(window);
window = nullptr;
return false;
if (!glcontext)
contexterror = std::string(SDL_GetError());
// Make sure the context's version is at least what we requested.
if (glcontext && !checkGLVersion(*attribs, glversion))
{
SDL_GL_DeleteContext(glcontext);
glcontext = nullptr;
}
if (!glcontext)
{
SDL_DestroyWindow(window);
window = nullptr;
return false;
}
}
}
@@ -596,16 +602,18 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
{
if (renderer == graphics::RENDERER_OPENGL)
sdlflags |= SDL_WINDOW_OPENGL;
#ifdef LOVE_GRAPHICS_METAL
if (renderer == graphics::RENDERER_METAL)
sdlflags |= SDL_WINDOW_METAL;
#endif
if (f.resizable)
if (renderer == graphics::RENDERER_VULKAN)
sdlflags |= SDL_WINDOW_VULKAN;
if (f.resizable)
sdlflags |= SDL_WINDOW_RESIZABLE;
if (f.borderless)
if (f.borderless)
sdlflags |= SDL_WINDOW_BORDERLESS;
// Note: this flag is ignored on Windows.
@@ -1110,6 +1118,10 @@ void Window::setVSync(int vsync)
SDL_GL_SetSwapInterval(1);
}
#ifdef LOVE_GRAPHICS_VULKAN
love::graphics::vulkan::Vulkan::setVsync(vsync);
#endif
#if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS)
if (metalView != nullptr)
{