Initial work on Vulkan support for Citra.
Signed-off-by: Patrick Martin <patrick.martin.r@gmail.com>
This commit is contained in:
parent
0d086616d1
commit
1ac50c17e8
12 changed files with 866 additions and 35 deletions
|
@ -42,6 +42,12 @@ option(ENABLE_QT "Enable the Qt frontend" ON)
|
|||
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
|
||||
option(CITRA_FORCE_QT4 "Use Qt4 even if Qt5 is available." OFF)
|
||||
|
||||
if (NOT ENABLE_QT)
|
||||
option(ENABLE_VULKAN "Enable Vulkan support." OFF)
|
||||
else()
|
||||
set(ENABLE_VULKAN OFF)
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
|
@ -136,9 +142,14 @@ find_package(OpenGL REQUIRED)
|
|||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
|
||||
if (ENABLE_GLFW)
|
||||
if (ENABLE_VULKAN)
|
||||
add_definitions(-DGLFW_INCLUDE_VULKAN)
|
||||
endif()
|
||||
if (CITRA_USE_BUNDLED_GLFW)
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
if (ENABLE_VULKAN)
|
||||
message(FATAL_ERROR "No beta GLFW binaries are available bundled, please install glfw 3.2 manually.")
|
||||
elseif (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(GLFW_VER "glfw-3.1.1-msvc2015_64")
|
||||
elseif (MSVC12 AND ARCHITECTURE_x86_64)
|
||||
set(GLFW_VER "glfw-3.1.1-msvc2013_64")
|
||||
|
@ -211,6 +222,13 @@ if (ENABLE_QT)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
find_package(Vulkan REQUIRED)
|
||||
include_directories(${Vulkan_INCLUDE_DIRS})
|
||||
set(VK_LIBRARYS ${Vulkan_LIBRARIES})
|
||||
add_definitions(-DVKENABLED)
|
||||
endif()
|
||||
|
||||
# This function should be passed a list of all files in a target. It will automatically generate
|
||||
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
|
||||
# one in the filesystem.
|
||||
|
|
32
externals/cmake-modules/FindVulkan.cmake
vendored
Normal file
32
externals/cmake-modules/FindVulkan.cmake
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# - Try to find Vulkan SDK
|
||||
# Once done, this will define
|
||||
#
|
||||
# Vulkan_FOUND - system has Vulkan SDK
|
||||
# Vulkan_INCLUDE_DIRS - the Vulkan include directories
|
||||
# Vulkan_LIBRARIES - link these to use Vulkan
|
||||
|
||||
include(LibFindMacros)
|
||||
|
||||
# Dependencies
|
||||
#libfind_package(PACKAGE DEPENDANCY)
|
||||
|
||||
# Use pkg-config to get hints about paths
|
||||
libfind_pkg_check_modules(Vulkan_PKGCONF Vulkan)
|
||||
|
||||
# Include dir
|
||||
find_path(Vulkan_INCLUDE_DIR
|
||||
NAMES vulkan/vulkan.h
|
||||
PATHS ${Vulkan_PKGCONF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Finally the library itself
|
||||
find_library(Vulkan_LIBRARY
|
||||
NAMES vulkan-1
|
||||
PATHS ${Vulkan_PKGCONF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Set the include dir variables and the libraries and let libfind_process do the rest.
|
||||
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
|
||||
set(Vulkan_PROCESS_INCLUDES Vulkan_INCLUDE_DIR Vulkan_INCLUDE_DIRS)
|
||||
set(Vulkan_PROCESS_LIBS Vulkan_LIBRARY Vulkan_LIBRARIES)
|
||||
libfind_process(Vulkan)
|
265
externals/cmake-modules/LibFindMacros.cmake
vendored
Normal file
265
externals/cmake-modules/LibFindMacros.cmake
vendored
Normal file
|
@ -0,0 +1,265 @@
|
|||
# Version 2.2
|
||||
# Public Domain, originally written by Lasse Kärkkäinen <tronic>
|
||||
# Maintained at https://github.com/Tronic/cmake-modules
|
||||
# Please send your improvements as pull requests on Github.
|
||||
|
||||
# Find another package and make it a dependency of the current package.
|
||||
# This also automatically forwards the "REQUIRED" argument.
|
||||
# Usage: libfind_package(<prefix> <another package> [extra args to find_package])
|
||||
macro (libfind_package PREFIX PKG)
|
||||
set(${PREFIX}_args ${PKG} ${ARGN})
|
||||
if (${PREFIX}_FIND_REQUIRED)
|
||||
set(${PREFIX}_args ${${PREFIX}_args} REQUIRED)
|
||||
endif()
|
||||
find_package(${${PREFIX}_args})
|
||||
set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG})
|
||||
unset(${PREFIX}_args)
|
||||
endmacro()
|
||||
|
||||
# A simple wrapper to make pkg-config searches a bit easier.
|
||||
# Works the same as CMake's internal pkg_check_modules but is always quiet.
|
||||
macro (libfind_pkg_check_modules)
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(${ARGN} QUIET)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Avoid useless copy&pasta by doing what most simple libraries do anyway:
|
||||
# pkg-config, find headers, find library.
|
||||
# Usage: libfind_pkg_detect(<prefix> <pkg-config args> FIND_PATH <name> [other args] FIND_LIBRARY <name> [other args])
|
||||
# E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2)
|
||||
function (libfind_pkg_detect PREFIX)
|
||||
# Parse arguments
|
||||
set(argname pkgargs)
|
||||
foreach (i ${ARGN})
|
||||
if ("${i}" STREQUAL "FIND_PATH")
|
||||
set(argname pathargs)
|
||||
elseif ("${i}" STREQUAL "FIND_LIBRARY")
|
||||
set(argname libraryargs)
|
||||
else()
|
||||
set(${argname} ${${argname}} ${i})
|
||||
endif()
|
||||
endforeach()
|
||||
if (NOT pkgargs)
|
||||
message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.")
|
||||
endif()
|
||||
# Find library
|
||||
libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs})
|
||||
if (pathargs)
|
||||
find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS})
|
||||
endif()
|
||||
if (libraryargs)
|
||||
find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Extracts a version #define from a version.h file, output stored to <PREFIX>_VERSION.
|
||||
# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR)
|
||||
# Fourth argument "QUIET" may be used for silently testing different define names.
|
||||
# This function does nothing if the version variable is already defined.
|
||||
function (libfind_version_header PREFIX VERSION_H DEFINE_NAME)
|
||||
# Skip processing if we already have a version or if the include dir was not found
|
||||
if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR)
|
||||
return()
|
||||
endif()
|
||||
set(quiet ${${PREFIX}_FIND_QUIETLY})
|
||||
# Process optional arguments
|
||||
foreach(arg ${ARGN})
|
||||
if (arg STREQUAL "QUIET")
|
||||
set(quiet TRUE)
|
||||
else()
|
||||
message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.")
|
||||
endif()
|
||||
endforeach()
|
||||
# Read the header and parse for version number
|
||||
set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
|
||||
if (NOT EXISTS ${filename})
|
||||
if (NOT quiet)
|
||||
message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
file(READ "${filename}" header)
|
||||
string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}")
|
||||
# No regex match?
|
||||
if (match STREQUAL header)
|
||||
if (NOT quiet)
|
||||
message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"<version>\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
# Export the version string
|
||||
set(${PREFIX}_VERSION "${match}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Do the final processing once the paths have been detected.
|
||||
# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
|
||||
# all the variables, each of which contain one include directory.
|
||||
# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
|
||||
# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
|
||||
# Also handles errors in case library detection was required, etc.
|
||||
function (libfind_process PREFIX)
|
||||
# Skip processing if already processed during this configuration run
|
||||
if (${PREFIX}_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(found TRUE) # Start with the assumption that the package was found
|
||||
|
||||
# Did we find any files? Did we miss includes? These are for formatting better error messages.
|
||||
set(some_files FALSE)
|
||||
set(missing_headers FALSE)
|
||||
|
||||
# Shorthands for some variables that we need often
|
||||
set(quiet ${${PREFIX}_FIND_QUIETLY})
|
||||
set(required ${${PREFIX}_FIND_REQUIRED})
|
||||
set(exactver ${${PREFIX}_FIND_VERSION_EXACT})
|
||||
set(findver "${${PREFIX}_FIND_VERSION}")
|
||||
set(version "${${PREFIX}_VERSION}")
|
||||
|
||||
# Lists of config option names (all, includes, libs)
|
||||
unset(configopts)
|
||||
set(includeopts ${${PREFIX}_PROCESS_INCLUDES})
|
||||
set(libraryopts ${${PREFIX}_PROCESS_LIBS})
|
||||
|
||||
# Process deps to add to
|
||||
foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES})
|
||||
if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS)
|
||||
# The package seems to export option lists that we can use, woohoo!
|
||||
list(APPEND includeopts ${${i}_INCLUDE_OPTS})
|
||||
list(APPEND libraryopts ${${i}_LIBRARY_OPTS})
|
||||
else()
|
||||
# If plural forms don't exist or they equal singular forms
|
||||
if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR
|
||||
({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES))
|
||||
# Singular forms can be used
|
||||
if (DEFINED ${i}_INCLUDE_DIR)
|
||||
list(APPEND includeopts ${i}_INCLUDE_DIR)
|
||||
endif()
|
||||
if (DEFINED ${i}_LIBRARY)
|
||||
list(APPEND libraryopts ${i}_LIBRARY)
|
||||
endif()
|
||||
else()
|
||||
# Oh no, we don't know the option names
|
||||
message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (includeopts)
|
||||
list(REMOVE_DUPLICATES includeopts)
|
||||
endif()
|
||||
|
||||
if (libraryopts)
|
||||
list(REMOVE_DUPLICATES libraryopts)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}")
|
||||
if (NOT tmp STREQUAL "${includeopts} ${libraryopts}")
|
||||
message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).")
|
||||
endif()
|
||||
|
||||
# Include/library names separated by spaces (notice: not CMake lists)
|
||||
unset(includes)
|
||||
unset(libs)
|
||||
|
||||
# Process all includes and set found false if any are missing
|
||||
foreach (i ${includeopts})
|
||||
list(APPEND configopts ${i})
|
||||
if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
|
||||
list(APPEND includes "${${i}}")
|
||||
else()
|
||||
set(found FALSE)
|
||||
set(missing_headers TRUE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Process all libraries and set found false if any are missing
|
||||
foreach (i ${libraryopts})
|
||||
list(APPEND configopts ${i})
|
||||
if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
|
||||
list(APPEND libs "${${i}}")
|
||||
else()
|
||||
set (found FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Version checks
|
||||
if (found AND findver)
|
||||
if (NOT version)
|
||||
message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.")
|
||||
elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver))
|
||||
set(found FALSE)
|
||||
set(version_unsuitable TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If all-OK, hide all config options, export variables, print status and exit
|
||||
if (found)
|
||||
foreach (i ${configopts})
|
||||
mark_as_advanced(${i})
|
||||
endforeach()
|
||||
if (NOT quiet)
|
||||
message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
|
||||
if (LIBFIND_DEBUG)
|
||||
message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}")
|
||||
message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}")
|
||||
message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}")
|
||||
message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}")
|
||||
message(STATUS " ${PREFIX}_LIBRARIES=${libs}")
|
||||
endif()
|
||||
set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE)
|
||||
set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE)
|
||||
set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE)
|
||||
set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE)
|
||||
set (${PREFIX}_FOUND TRUE PARENT_SCOPE)
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Format messages for debug info and the type of error
|
||||
set(vars "Relevant CMake configuration variables:\n")
|
||||
foreach (i ${configopts})
|
||||
mark_as_advanced(CLEAR ${i})
|
||||
set(val ${${i}})
|
||||
if ("${val}" STREQUAL "${i}-NOTFOUND")
|
||||
set (val "<not found>")
|
||||
elseif (val AND NOT EXISTS ${val})
|
||||
set (val "${val} (does not exist)")
|
||||
else()
|
||||
set(some_files TRUE)
|
||||
endif()
|
||||
set(vars "${vars} ${i}=${val}\n")
|
||||
endforeach()
|
||||
set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n")
|
||||
if (version_unsuitable)
|
||||
set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but")
|
||||
if (exactver)
|
||||
set(msg "${msg} only version ${findver} is acceptable.")
|
||||
else()
|
||||
set(msg "${msg} version ${findver} is the minimum requirement.")
|
||||
endif()
|
||||
else()
|
||||
if (missing_headers)
|
||||
set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?")
|
||||
elseif (some_files)
|
||||
set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?")
|
||||
if(findver)
|
||||
set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).")
|
||||
endif()
|
||||
else()
|
||||
set(msg "We were unable to find package ${PREFIX}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Fatal error out if REQUIRED
|
||||
if (required)
|
||||
set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.")
|
||||
message(FATAL_ERROR "${msg}\n${vars}")
|
||||
endif()
|
||||
# Otherwise just print a nasty warning
|
||||
if (NOT quiet)
|
||||
message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}")
|
||||
endif()
|
||||
endfunction()
|
|
@ -18,7 +18,7 @@ link_directories(${GLFW_LIBRARY_DIRS})
|
|||
|
||||
add_executable(citra ${SRCS} ${HEADERS})
|
||||
target_link_libraries(citra core video_core common)
|
||||
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
|
||||
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} ${VK_LIBRARYS} inih glad)
|
||||
if (MSVC)
|
||||
target_link_libraries(citra getopt)
|
||||
endif()
|
||||
|
|
|
@ -8,9 +8,18 @@
|
|||
|
||||
// Let’s use our own GL header, instead of one from GLFW.
|
||||
#include <glad/glad.h>
|
||||
#ifdef VKENABLED
|
||||
#ifdef _WIN32
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#else
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#endif
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/key_map.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -27,7 +36,26 @@
|
|||
EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
|
||||
return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
|
||||
}
|
||||
|
||||
#ifdef VKENABLED
|
||||
bool EmuWindow_GLFW::VulkanSupported(){
|
||||
return glfwVulkanSupported() == GLFW_TRUE;
|
||||
}
|
||||
bool EmuWindow_GLFW::CanDevicePresent(void * instance, void * device,uint32_t queue){
|
||||
return glfwGetPhysicalDevicePresentationSupport((VkInstance)instance,(VkPhysicalDevice)device,queue);
|
||||
}
|
||||
void * EmuWindow_GLFW::CreateVulkanSurface(void * instance){
|
||||
VkSurfaceKHR surface;
|
||||
if(glfwCreateWindowSurface((VkInstance)instance, this->m_render_window, VK_NULL_HANDLE, &surface)==VK_SUCCESS)
|
||||
return surface;
|
||||
return NULL;
|
||||
}
|
||||
void EmuWindow_GLFW::DestroyVulkanSurface(void * instance, void * surface){
|
||||
vkDestroySurfaceKHR((VkInstance)instance, (VkSurfaceKHR)surface, VK_NULL_HANDLE);
|
||||
}
|
||||
const char** EmuWindow_GLFW::RequiredVulkanExtensions(uint32_t * count){
|
||||
return glfwGetRequiredInstanceExtensions(count);
|
||||
}
|
||||
#endif
|
||||
void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
|
||||
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||
auto emu_window = GetEmuWindow(win);
|
||||
|
@ -88,28 +116,49 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
|
|||
LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
|
||||
exit(1);
|
||||
}
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
// GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
|
||||
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
|
||||
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
|
||||
window_title.c_str(), nullptr, nullptr);
|
||||
|
||||
if (m_render_window == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glfwSetWindowUserPointer(m_render_window, this);
|
||||
|
||||
// Notify base interface about window state
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(m_render_window, &width, &height);
|
||||
OnFramebufferResizeEvent(m_render_window, width, height);
|
||||
#ifdef VKENABLED
|
||||
if (!VulkanSupported()){
|
||||
#endif
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
// GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
|
||||
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
|
||||
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
|
||||
window_title.c_str(), nullptr, nullptr);
|
||||
|
||||
if (m_render_window == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glfwSetWindowUserPointer(m_render_window, this);
|
||||
|
||||
// Notify base interface about window state
|
||||
glfwGetFramebufferSize(m_render_window, &width, &height);
|
||||
OnFramebufferResizeEvent(m_render_window, width, height);
|
||||
|
||||
#ifdef VKENABLED
|
||||
}else{
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
|
||||
std::string window_title = Common::StringFromFormat("Citra | %s-%s (Vulkan)", Common::g_scm_branch, Common::g_scm_desc);
|
||||
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
|
||||
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
|
||||
window_title.c_str(), nullptr, nullptr);
|
||||
|
||||
if (m_render_window == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glfwSetWindowUserPointer(m_render_window, this);
|
||||
}
|
||||
#endif
|
||||
|
||||
glfwGetWindowSize(m_render_window, &width, &height);
|
||||
OnClientAreaResizeEvent(m_render_window, width, height);
|
||||
|
|
|
@ -41,7 +41,13 @@ public:
|
|||
static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
|
||||
|
||||
void ReloadSetKeymaps() override;
|
||||
|
||||
#ifdef VKENABLED
|
||||
bool VulkanSupported() override;
|
||||
bool CanDevicePresent(void * instance, void * device,uint32_t queue) override;
|
||||
void * CreateVulkanSurface(void * instance) override;
|
||||
void DestroyVulkanSurface(void * instance, void * surface) override;
|
||||
const char** RequiredVulkanExtensions(uint32_t * count) override;
|
||||
#endif
|
||||
private:
|
||||
void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
|
||||
|
||||
|
|
|
@ -11,6 +11,23 @@
|
|||
#include "emu_window.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#ifdef VKENABLED
|
||||
bool EmuWindow::VulkanSupported(){
|
||||
return false;
|
||||
}
|
||||
bool EmuWindow::CanDevicePresent(void * instance, void* device,uint32_t queue){
|
||||
return false;
|
||||
}
|
||||
void * EmuWindow::CreateVulkanSurface(void * instance){
|
||||
return NULL;
|
||||
}
|
||||
void EmuWindow::DestroyVulkanSurface(void * instance, void * surface){}
|
||||
const char** EmuWindow::RequiredVulkanExtensions(uint32_t * count){
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
|
||||
pad_state.hex |= KeyMap::GetPadKey(key).hex;
|
||||
}
|
||||
|
|
|
@ -143,7 +143,28 @@ public:
|
|||
const FramebufferLayout& GetFramebufferLayout() const {
|
||||
return framebuffer_layout;
|
||||
}
|
||||
|
||||
#ifdef VKENABLED
|
||||
/**
|
||||
* Query if the vulkan api is supported.
|
||||
*/
|
||||
virtual bool VulkanSupported();
|
||||
/**
|
||||
* Check if the device can present.
|
||||
*/
|
||||
virtual bool CanDevicePresent(void* instance, void* device,uint32_t queue);
|
||||
/**
|
||||
* Gets a vulkan surface for this emu window returns NULL if unsupported.
|
||||
*/
|
||||
virtual void * CreateVulkanSurface(void * instance);
|
||||
/**
|
||||
* Destroys the passed vulkan surface for the passed instance and this emu window.
|
||||
*/
|
||||
virtual void DestroyVulkanSurface(void * instance, void * surface);
|
||||
/**
|
||||
* Lists all required vulkan extensions for this emu window
|
||||
*/
|
||||
virtual const char** RequiredVulkanExtensions(uint32_t * count);
|
||||
#endif
|
||||
protected:
|
||||
EmuWindow() {
|
||||
// TODO: Find a better place to set this.
|
||||
|
|
|
@ -1,10 +1,32 @@
|
|||
set(SRCS
|
||||
if(ENABLE_VULKAN)
|
||||
set(RENDER_VK_SRCS
|
||||
renderer_vulkan/renderer_vulkan.cpp
|
||||
)
|
||||
set(RENDER_VK_HEADERS
|
||||
renderer_vulkan/renderer_vulkan.h
|
||||
)
|
||||
endif()
|
||||
set(RENDER_SRCS
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
${RENDER_VK_SRCS}
|
||||
)
|
||||
set(RENDER_HEADERS
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/pica_to_gl.h
|
||||
renderer_opengl/renderer_opengl.h
|
||||
${RENDER_VK_HEADERS}
|
||||
)
|
||||
set(SRCS ${RENDER_SRCS}
|
||||
debug_utils/debug_utils.cpp
|
||||
clipper.cpp
|
||||
command_processor.cpp
|
||||
|
@ -21,14 +43,7 @@ set(SRCS
|
|||
|
||||
set(HEADERS
|
||||
debug_utils/debug_utils.h
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/pica_to_gl.h
|
||||
renderer_opengl/renderer_opengl.h
|
||||
${RENDER_HEADERS}
|
||||
clipper.h
|
||||
command_processor.h
|
||||
gpu_debugger.h
|
||||
|
|
314
src/video_core/renderer_vulkan/renderer_vulkan.cpp
Normal file
314
src/video_core/renderer_vulkan/renderer_vulkan.cpp
Normal file
|
@ -0,0 +1,314 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/emu_window.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler_reporting.h"
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "core/hw/hw.h"
|
||||
#include "core/hw/lcd.h"
|
||||
|
||||
#include "video_core/video_core.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
|
||||
#include "renderer_vulkan.h"
|
||||
|
||||
#define INIT_INSTANCE 1
|
||||
#define INIT_DEVICE 2
|
||||
#define INIT_SURFACE 4
|
||||
#define INIT_SWAPCHAIN 8
|
||||
#define INIT_TOPSCREEN 16
|
||||
#define INIT_BOTTOMSCREEN 32
|
||||
#define INIT_MEMORYTOP 64
|
||||
#define INIT_MEMORYBOTTOM 128
|
||||
#define INIT_VIEWTOP 256
|
||||
#define INIT_VIEWBOTTOM 512
|
||||
|
||||
static const char vertex_shader[] = R"(
|
||||
#version 150 core
|
||||
|
||||
in vec2 vert_position;
|
||||
in vec2 vert_tex_coord;
|
||||
out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragment_shader[] = R"(
|
||||
#version 150 core
|
||||
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
||||
)";
|
||||
|
||||
inline bool HasPicaFeatures(VkPhysicalDeviceFeatures * features){
|
||||
return features->geometryShader;//&& features->
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
|
||||
* corner and (width, height) on the lower-bottom.
|
||||
*
|
||||
* The projection part of the matrix is trivial, hence these operations are represented
|
||||
* by a 3x2 matrix.
|
||||
*/
|
||||
static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height) {
|
||||
std::array<GLfloat, 3 * 2> matrix;
|
||||
|
||||
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
|
||||
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
|
||||
// Last matrix row is implicitly assumed to be [0, 0, 1].
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
RendererVulkan::RendererVulkan(){
|
||||
resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
|
||||
resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
|
||||
}
|
||||
|
||||
void RendererVulkan::SetWindow(EmuWindow* window){
|
||||
this->render_window = window;
|
||||
}
|
||||
|
||||
void RendererVulkan::Init(){
|
||||
VkApplicationInfo appinfo;
|
||||
appinfo.apiVersion = VK_API_VERSION;
|
||||
appinfo.applicationVersion = 0;
|
||||
appinfo.pApplicationName = "CTR";
|
||||
appinfo.pEngineName = "Citra Emulator";
|
||||
appinfo.pNext = VK_NULL_HANDLE;
|
||||
appinfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
uint32_t extensionCount;
|
||||
const char ** enabledExtensions = render_window->RequiredVulkanExtensions(&extensionCount);
|
||||
VkInstanceCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
info.pNext = VK_NULL_HANDLE;
|
||||
info.ppEnabledLayerNames = VK_NULL_HANDLE;
|
||||
info.enabledLayerCount = 0;
|
||||
info.pApplicationInfo = &appinfo;
|
||||
info.enabledExtensionCount = extensionCount;
|
||||
info.ppEnabledExtensionNames = enabledExtensions;
|
||||
VkResult result = vkCreateInstance(&info, VK_NULL_HANDLE, &instance);
|
||||
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_INSTANCE;
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, VK_NULL_HANDLE);
|
||||
VkPhysicalDevice * devices = (VkPhysicalDevice*)malloc(deviceCount*sizeof(VkPhysicalDevice));
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, devices);
|
||||
VkPhysicalDeviceProperties deviceProps;
|
||||
VkPhysicalDeviceFeatures deviceFeatures;
|
||||
uint32_t lastAPIVersion = 0;
|
||||
VkPhysicalDevice device = VK_NULL_HANDLE;
|
||||
for (int i = 0; i < deviceCount; i++){
|
||||
vkGetPhysicalDeviceProperties(devices[i], &deviceProps);
|
||||
vkGetPhysicalDeviceFeatures(devices[i], &deviceFeatures);
|
||||
if (deviceProps.apiVersion > lastAPIVersion && HasPicaFeatures(&deviceFeatures)){
|
||||
device = devices[i];
|
||||
}
|
||||
}
|
||||
if (device != VK_NULL_HANDLE){
|
||||
VkDeviceCreateInfo deviceInfo;
|
||||
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
|
||||
deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
deviceInfo.pEnabledFeatures = &deviceFeatures;
|
||||
deviceInfo.enabledExtensionCount = 0;
|
||||
deviceInfo.enabledLayerCount = 0;
|
||||
deviceInfo.queueCreateInfoCount = 0;
|
||||
deviceInfo.ppEnabledExtensionNames = VK_NULL_HANDLE;
|
||||
deviceInfo.ppEnabledLayerNames = VK_NULL_HANDLE;
|
||||
deviceInfo.pNext = VK_NULL_HANDLE;
|
||||
deviceInfo.pQueueCreateInfos = VK_NULL_HANDLE;
|
||||
if (vkCreateDevice(device, &deviceInfo, VK_NULL_HANDLE, &this->device) == VK_SUCCESS){
|
||||
this->Initialized |= INIT_DEVICE;
|
||||
surface = (VkSurfaceKHR)render_window->CreateVulkanSurface(instance);
|
||||
if (surface != NULL){
|
||||
this->Initialized |= INIT_SURFACE;
|
||||
VkSwapchainCreateInfoKHR swapchainInfo = {};
|
||||
swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainInfo.imageFormat = VK_FORMAT_R8G8B8_UINT;
|
||||
swapchainInfo.surface = surface;
|
||||
swapchainInfo.minImageCount = 2;
|
||||
swapchainInfo.clipped = true;
|
||||
swapchainInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
vkCreateSwapchainKHR(this->device, &swapchainInfo, VK_NULL_HANDLE, &swapchain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RendererVulkan::InitVulkanObjects(){
|
||||
if (this->Initialized & INIT_INSTANCE){
|
||||
VkImageCreateInfo imageTop = {};
|
||||
VkImageCreateInfo imageBottom = {};
|
||||
|
||||
imageTop.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageTop.pNext = VK_NULL_HANDLE;
|
||||
imageTop.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageTop.format = VK_FORMAT_R8G8B8_UINT;
|
||||
imageTop.extent = { VideoCore::kScreenTopHeight, VideoCore::kScreenTopWidth, 1 };
|
||||
imageTop.mipLevels = 1;
|
||||
imageTop.arrayLayers = 1;
|
||||
imageBottom.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageTop.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageTop.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
|
||||
imageBottom.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageBottom.pNext = VK_NULL_HANDLE;
|
||||
imageBottom.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageBottom.format = VK_FORMAT_R8G8B8_UINT;
|
||||
imageBottom.extent = { VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight, 1 };
|
||||
imageBottom.mipLevels = 1;
|
||||
imageBottom.arrayLayers = 1;
|
||||
imageBottom.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageBottom.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageBottom.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
|
||||
VkResult result = vkCreateImage(device, &imageTop, VK_NULL_HANDLE, &textures[0].texture);
|
||||
bool success = true;
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_TOPSCREEN;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
result = vkCreateImage(device, &imageBottom, VK_NULL_HANDLE, &textures[1].texture);
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_BOTTOMSCREEN;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
if (success){
|
||||
VkMemoryAllocateInfo imageTopAllocation;
|
||||
VkMemoryRequirements imageTopMemoryReqs;
|
||||
VkMemoryAllocateInfo imageBottomAllocation;
|
||||
VkMemoryRequirements imageBottomMemoryReqs;
|
||||
vkGetImageMemoryRequirements(device, textures[0].texture, &imageTopMemoryReqs);
|
||||
vkGetImageMemoryRequirements(device, textures[1].texture, &imageBottomMemoryReqs);
|
||||
imageTopAllocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
imageTopAllocation.pNext = VK_NULL_HANDLE;
|
||||
imageTopAllocation.allocationSize = imageTopMemoryReqs.size;
|
||||
imageTopAllocation.memoryTypeIndex = 0; //TODO: figgure out what goes here.
|
||||
result = vkAllocateMemory(device,&imageTopAllocation,VK_NULL_HANDLE,&textures[0].memory);
|
||||
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_MEMORYTOP;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
|
||||
imageBottomAllocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
imageBottomAllocation.pNext = VK_NULL_HANDLE;
|
||||
imageBottomAllocation.allocationSize = imageBottomMemoryReqs.size;
|
||||
imageBottomAllocation.memoryTypeIndex = 0; //TODO: figgure out what goes here.
|
||||
result = vkAllocateMemory(device,&imageBottomAllocation,VK_NULL_HANDLE,&textures[0].memory);
|
||||
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_MEMORYBOTTOM;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success){
|
||||
VkImageViewCreateInfo viewTopInfo;
|
||||
VkImageViewCreateInfo viewBottomInfo;
|
||||
|
||||
viewTopInfo.image = textures[0].texture;
|
||||
viewBottomInfo.image = textures[1].texture;
|
||||
|
||||
viewTopInfo.format = VK_FORMAT_R8G8B8_UINT;
|
||||
viewBottomInfo.format = VK_FORMAT_R8G8B8_UINT;
|
||||
|
||||
viewTopInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewBottomInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
|
||||
result = vkCreateImageView(device,&viewTopInfo,VK_NULL_HANDLE,&textures[0].view);
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_VIEWTOP;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
result = vkCreateImageView(device,&viewBottomInfo,VK_NULL_HANDLE,&textures[1].view);
|
||||
if (result == VK_SUCCESS){
|
||||
this->Initialized |= INIT_VIEWBOTTOM;
|
||||
}else{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void RendererVulkan::SwapBuffers() {
|
||||
|
||||
for (int i : {0, 1}) {
|
||||
const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
|
||||
|
||||
u32 lcd_color_addr = (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
|
||||
lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
|
||||
LCD::Regs::ColorFill color_fill = {0};
|
||||
LCD::Read(color_fill.raw, lcd_color_addr);
|
||||
if (color_fill.is_enabled) {
|
||||
}
|
||||
else{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the emulated screens to the emulator window.
|
||||
*/
|
||||
void RendererVulkan::DrawScreens() {
|
||||
auto layout = render_window->GetFramebufferLayout();
|
||||
DrawSingleScreenRotated(textures[0], (float)layout.top_screen.left, (float)layout.top_screen.top,
|
||||
(float)layout.top_screen.GetWidth(), (float)layout.top_screen.GetHeight());
|
||||
DrawSingleScreenRotated(textures[1], (float)layout.bottom_screen.left,(float)layout.bottom_screen.top,
|
||||
(float)layout.bottom_screen.GetWidth(), (float)layout.bottom_screen.GetHeight());
|
||||
|
||||
m_current_frame++;
|
||||
}
|
||||
|
||||
RendererVulkan::~RendererVulkan(){
|
||||
if (this->Initialized&INIT_VIEWBOTTOM)vkDestroyImageView(device, textures[1].view, VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_VIEWTOP)vkDestroyImageView(device, textures[0].view, VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_MEMORYBOTTOM)vkFreeMemory(this->device, this->textures[1].memory, VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_MEMORYTOP)vkFreeMemory(this->device, this->textures[0].memory, VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_BOTTOMSCREEN)vkDestroyImage(this->device,this->textures[1].texture,VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_TOPSCREEN)vkDestroyImage(this->device,this->textures[0].texture,VK_NULL_HANDLE);
|
||||
//if (this->Initialized&INIT_SWAPCHAIN)vkDestroySwapchainKHR(device, swapchain, VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_SURFACE)render_window->DestroyVulkanSurface(instance, surface);
|
||||
if (this->Initialized&INIT_DEVICE)vkDestroyDevice(this->device,VK_NULL_HANDLE);
|
||||
if (this->Initialized&INIT_INSTANCE)vkDestroyInstance(this->instance,VK_NULL_HANDLE);
|
||||
}
|
83
src/video_core/renderer_vulkan/renderer_vulkan.h
Normal file
83
src/video_core/renderer_vulkan/renderer_vulkan.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "core/hw/gpu.h"
|
||||
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#else
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#endif
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
class EmuWindow;
|
||||
|
||||
class RendererVulkan : public RendererBase {
|
||||
public:
|
||||
|
||||
RendererVulkan();
|
||||
~RendererVulkan() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void SwapBuffers() override;
|
||||
|
||||
/**
|
||||
* Set the emulator window to use for renderer
|
||||
* @param window EmuWindow handle to emulator window to use for rendering
|
||||
*/
|
||||
void SetWindow(EmuWindow* window) override;
|
||||
|
||||
/// Initialize the renderer
|
||||
void Init() override;
|
||||
|
||||
/// Shutdown the renderer
|
||||
void ShutDown() override;
|
||||
|
||||
private:
|
||||
VkSurfaceKHR screen;
|
||||
/// Structure used for storing information about the textures for each 3DS screen
|
||||
struct TextureInfo {
|
||||
VkSampler sampler;
|
||||
VkImage texture;
|
||||
VkImageLayout layout;
|
||||
VkDeviceMemory memory;
|
||||
VkImageView view;
|
||||
};
|
||||
|
||||
void InitVulkanObjects();
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
const GPU::Regs::FramebufferConfig& framebuffer);
|
||||
void DrawScreens();
|
||||
void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h);
|
||||
void UpdateFramerate();
|
||||
|
||||
// Loads framebuffer from emulated memory into the active Vulkan texture.
|
||||
void LoadFBToActiveVKTexture(const GPU::Regs::FramebufferConfig& framebuffer,
|
||||
const TextureInfo& texture);
|
||||
// Fills active OpenGL texture with the given RGB color.
|
||||
void LoadColorToActiveVKTexture(u8 color_r, u8 color_g, u8 color_b,
|
||||
const TextureInfo& texture);
|
||||
|
||||
short Initialized = 0;
|
||||
|
||||
EmuWindow* render_window; ///< Handle to render window
|
||||
|
||||
int resolution_width; ///< Current resolution width
|
||||
int resolution_height; ///< Current resolution height
|
||||
|
||||
VkInstance instance;
|
||||
VkDevice device;
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkSwapchainKHR swapchain;
|
||||
std::array<TextureInfo, 2> textures; ///< Textures for top and bottom screens respectively
|
||||
};
|
|
@ -14,6 +14,9 @@
|
|||
#include "video_core/pica.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
#ifdef VKENABLED
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#endif
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -32,7 +35,15 @@ void Init(EmuWindow* emu_window) {
|
|||
Pica::Init();
|
||||
|
||||
g_emu_window = emu_window;
|
||||
#ifdef VKENABLED
|
||||
if (emu_window->VulkanSupported()){
|
||||
g_renderer = Common::make_unique<RendererVulkan>();
|
||||
}else{
|
||||
g_renderer = Common::make_unique<RendererOpenGL>();
|
||||
}
|
||||
#else
|
||||
g_renderer = Common::make_unique<RendererOpenGL>();
|
||||
#endif
|
||||
g_renderer->SetWindow(g_emu_window);
|
||||
g_renderer->Init();
|
||||
|
||||
|
|
Loading…
Reference in a new issue