diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt
index 053cf691d..f895e53b4 100644
--- a/src/qt/CMakeLists.txt
+++ b/src/qt/CMakeLists.txt
@@ -145,6 +145,12 @@ add_library(ui STATIC
qt_unixmanagerfilter.cpp
qt_unixmanagerfilter.hpp
+ qt_vulkanwindowrenderer.hpp
+ qt_vulkanwindowrenderer.cpp
+
+ qt_vulkanrenderer.hpp
+ qt_vulkanrenderer.cpp
+
../qt_resources.qrc
)
diff --git a/src/qt/qt.c b/src/qt/qt.c
index 259f79c88..19ced376e 100644
--- a/src/qt/qt.c
+++ b/src/qt/qt.c
@@ -47,6 +47,8 @@ plat_vidapi(char* api) {
return 2;
} else if (!strcasecmp(api, "qt_opengl3")) {
return 3;
+ } else if (!strcasecmp(api, "qt_vulkan")) {
+ return 4;
}
return 0;
@@ -68,6 +70,9 @@ char* plat_vidapi_name(int api) {
case 3:
name = "qt_opengl3";
break;
+ case 4:
+ name = "qt_vulkan";
+ break;
default:
fatal("Unknown renderer: %i\n", api);
break;
diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp
index 2e38197de..f285b5745 100644
--- a/src/qt/qt_hardwarerenderer.cpp
+++ b/src/qt/qt_hardwarerenderer.cpp
@@ -150,6 +150,7 @@ void HardwareRenderer::paintGL() {
texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f));
texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f));
texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f));
+
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].bind(); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].write(0, verts.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].release();
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].bind(); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].write(0, texcoords.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].release();
diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp
index dce5ef470..d15151039 100644
--- a/src/qt/qt_mainwindow.cpp
+++ b/src/qt/qt_mainwindow.cpp
@@ -276,9 +276,15 @@ MainWindow::MainWindow(QWidget *parent) :
vid_api = 0;
ui->actionHardware_Renderer_OpenGL->setVisible(false);
ui->actionHardware_Renderer_OpenGL_ES->setVisible(false);
+ ui->actionVulkan->setVisible(false);
ui->actionOpenGL_3_0_Core->setVisible(false);
}
+#if !QT_CONFIG(vulkan)
+ if (vid_api == 4) vid_api = 0;
+ ui->actionVulkan->setVisible(false);
+#endif
+
QActionGroup* actGroup = nullptr;
actGroup = new QActionGroup(this);
@@ -286,6 +292,7 @@ MainWindow::MainWindow(QWidget *parent) :
actGroup->addAction(ui->actionHardware_Renderer_OpenGL);
actGroup->addAction(ui->actionHardware_Renderer_OpenGL_ES);
actGroup->addAction(ui->actionOpenGL_3_0_Core);
+ actGroup->addAction(ui->actionVulkan);
actGroup->setExclusive(true);
connect(actGroup, &QActionGroup::triggered, [this](QAction* action) {
@@ -304,6 +311,9 @@ MainWindow::MainWindow(QWidget *parent) :
case 3:
ui->stackedWidget->switchRenderer(RendererStack::Renderer::OpenGL3);
break;
+ case 4:
+ ui->stackedWidget->switchRenderer(RendererStack::Renderer::Vulkan);
+ break;
}
});
@@ -509,8 +519,11 @@ void MainWindow::closeEvent(QCloseEvent *event) {
}
qt_nvr_save();
config_save();
+
if (ui->stackedWidget->mouse_exit_func)
ui->stackedWidget->mouse_exit_func();
+
+ ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software);
event->accept();
}
diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui
index 60b77d290..4720afc2a 100644
--- a/src/qt/qt_mainwindow.ui
+++ b/src/qt/qt_mainwindow.ui
@@ -54,7 +54,7 @@
0
0
724
- 22
+ 23
diff --git a/src/qt/qt_renderercommon.hpp b/src/qt/qt_renderercommon.hpp
index 1f94781e4..a966b730f 100644
--- a/src/qt/qt_renderercommon.hpp
+++ b/src/qt/qt_renderercommon.hpp
@@ -20,6 +20,8 @@ public:
void onResize(int width, int height);
virtual void finalize() { }
+ virtual uint32_t getBytesPerRow() { return 2048 * 4; }
+
virtual std::vector> getBuffers() = 0;
/* Does renderer implement options dialog */
diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp
index a78202d05..2f1649126 100644
--- a/src/qt/qt_rendererstack.cpp
+++ b/src/qt/qt_rendererstack.cpp
@@ -24,13 +24,17 @@
#include "qt_hardwarerenderer.hpp"
#include "qt_openglrenderer.hpp"
#include "qt_softwarerenderer.hpp"
+#include "qt_vulkanwindowrenderer.hpp"
#include "qt_mainwindow.hpp"
#include "qt_util.hpp"
#include "evdev_mouse.hpp"
+#include
+
#include
+#include
#ifdef __APPLE__
# include
@@ -241,6 +245,7 @@ void
RendererStack::createRenderer(Renderer renderer)
{
switch (renderer) {
+ default:
case Renderer::Software:
{
auto sw = new SoftwareRenderer(this);
@@ -278,13 +283,13 @@ RendererStack::createRenderer(Renderer renderer)
rendererWindow = hw;
connect(this, &RendererStack::blitToRenderer, hw, &OpenGLRenderer::onBlit, Qt::QueuedConnection);
connect(hw, &OpenGLRenderer::initialized, [=]() {
- /* Buffers are awailable only after initialization. */
+ /* Buffers are available only after initialization. */
imagebufs = rendererWindow->getBuffers();
endblit();
emit rendererChanged();
});
connect(hw, &OpenGLRenderer::errorInitializing, [=]() {
- /* Renderer could initialize, fallback to software. */
+ /* Renderer not could initialize, fallback to software. */
imagebufs = {};
endblit();
QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); });
@@ -292,6 +297,43 @@ RendererStack::createRenderer(Renderer renderer)
current.reset(this->createWindowContainer(hw, this));
break;
}
+#if QT_CONFIG(vulkan)
+ case Renderer::Vulkan:
+ {
+ this->createWinId();
+ VulkanWindowRenderer *hw = nullptr;
+ try {
+ hw = new VulkanWindowRenderer(this);
+ } catch(std::runtime_error& e) {
+ auto msgBox = new QMessageBox(QMessageBox::Critical, "86Box", e.what() + QString("\nFalling back to software rendering."), QMessageBox::Ok);
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ msgBox->show();
+ imagebufs = {};
+ endblit();
+ QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); });
+ break;
+ };
+ rendererWindow = hw;
+ connect(this, &RendererStack::blitToRenderer, hw, &VulkanWindowRenderer::onBlit, Qt::QueuedConnection);
+ connect(hw, &VulkanWindowRenderer::rendererInitialized, [=]() {
+ /* Buffers are available only after initialization. */
+ imagebufs = rendererWindow->getBuffers();
+ endblit();
+ emit rendererChanged();
+ });
+ connect(hw, &VulkanWindowRenderer::errorInitializing, [=]() {
+ /* Renderer could not initialize, fallback to software. */
+ auto msgBox = new QMessageBox(QMessageBox::Critical, "86Box", QString("Failed to initialize Vulkan renderer.\nFalling back to software rendering."), QMessageBox::Ok);
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ msgBox->show();
+ imagebufs = {};
+ endblit();
+ QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); });
+ });
+ current.reset(this->createWindowContainer(hw, this));
+ break;
+ }
+#endif
}
current->setFocusPolicy(Qt::NoFocus);
@@ -302,7 +344,7 @@ RendererStack::createRenderer(Renderer renderer)
currentBuf = 0;
- if (renderer != Renderer::OpenGL3) {
+ if (renderer != Renderer::OpenGL3 && renderer != Renderer::Vulkan) {
imagebufs = rendererWindow->getBuffers();
endblit();
emit rendererChanged();
@@ -323,7 +365,7 @@ RendererStack::blit(int x, int y, int w, int h)
sh = this->h = h;
uint8_t *imagebits = std::get(imagebufs[currentBuf]);
for (int y1 = y; y1 < (y + h); y1++) {
- auto scanline = imagebits + (y1 * (2048) * 4) + (x * 4);
+ auto scanline = imagebits + (y1 * rendererWindow->getBytesPerRow()) + (x * 4);
video_copy(scanline, &(buffer32->line[y1][x]), w * 4);
}
diff --git a/src/qt/qt_rendererstack.hpp b/src/qt/qt_rendererstack.hpp
index 6e4fa23d4..45daf838d 100644
--- a/src/qt/qt_rendererstack.hpp
+++ b/src/qt/qt_rendererstack.hpp
@@ -44,7 +44,8 @@ public:
Software,
OpenGL,
OpenGLES,
- OpenGL3
+ OpenGL3,
+ Vulkan
};
void switchRenderer(Renderer renderer);
diff --git a/src/qt/qt_vulkanrenderer.cpp b/src/qt/qt_vulkanrenderer.cpp
new file mode 100644
index 000000000..bb1d8c3a3
--- /dev/null
+++ b/src/qt/qt_vulkanrenderer.cpp
@@ -0,0 +1,1007 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Cacodemon345
+** Copyright (C) 2017 The Qt Company Ltd.
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+#include
+#include
+#include "qt_vulkanrenderer.hpp"
+#if QT_CONFIG(vulkan)
+#include
+
+extern "C"
+{
+#include <86box/86box.h>
+}
+
+// Use a triangle strip to get a quad.
+//
+// Note that the vertex data and the projection matrix assume OpenGL. With
+// Vulkan Y is negated in clip space and the near/far plane is at 0/1 instead
+// of -1/1. These will be corrected for by an extra transformation when
+// calculating the modelview-projection matrix.
+static float vertexData[] = { // Y up, front = CW
+ // x, y, z, u, v
+ -1, -1, 0, 0, 1,
+ -1, 1, 0, 0, 0,
+ 1, -1, 0, 1, 1,
+ 1, 1, 0, 1, 0
+};
+
+static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+VulkanRenderer2::VulkanRenderer2(QVulkanWindow *w)
+ : m_window(w)
+{
+}
+
+VkShaderModule VulkanRenderer2::createShader(const QString &name)
+{
+ QFile file(name);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to read shader %s", qPrintable(name));
+ return VK_NULL_HANDLE;
+ }
+ QByteArray blob = file.readAll();
+ file.close();
+
+ VkShaderModuleCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderInfo.codeSize = blob.size();
+ shaderInfo.pCode = reinterpret_cast(blob.constData());
+ VkShaderModule shaderModule;
+ VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create shader module: %d", err);
+ return VK_NULL_HANDLE;
+ }
+
+ return shaderModule;
+}
+
+bool VulkanRenderer2::createTexture()
+{
+ QImage img(2048, 2048, QImage::Format_RGBA8888_Premultiplied);
+ img.fill(QColor(0, 0, 0));
+
+ QVulkanFunctions *f = m_window->vulkanInstance()->functions();
+ VkDevice dev = m_window->device();
+
+ m_texFormat = VK_FORMAT_B8G8R8A8_SRGB;
+
+ // Now we can either map and copy the image data directly, or have to go
+ // through a staging buffer to copy and convert into the internal optimal
+ // tiling format.
+ VkFormatProperties props;
+ f->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props);
+ const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
+ const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
+ if (!canSampleLinear && !canSampleOptimal) {
+ qWarning("Neither linear nor optimal image sampling is supported for RGBA8");
+ return false;
+ }
+
+ static bool alwaysStage = qEnvironmentVariableIntValue("QT_VK_FORCE_STAGE_TEX");
+
+ if (canSampleLinear && !alwaysStage) {
+ if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
+ VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
+ m_window->hostVisibleMemoryIndex()))
+ return false;
+
+ if (!writeLinearImage(img, m_texImage, m_texMem))
+ return false;
+
+ m_texLayoutPending = true;
+ } else {
+ if (!createTextureImage(img.size(), &m_texStaging, &m_texStagingMem,
+ VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ m_window->hostVisibleMemoryIndex()))
+ return false;
+
+ if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
+ VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
+ m_window->deviceLocalMemoryIndex()))
+ return false;
+
+ if (!writeLinearImage(img, m_texStaging, m_texStagingMem))
+ return false;
+
+ m_texStagingPending = true;
+ }
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = m_texImage;
+ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = m_texFormat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.levelCount = viewInfo.subresourceRange.layerCount = 1;
+
+ VkResult err = m_devFuncs->vkCreateImageView(dev, &viewInfo, nullptr, &m_texView);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view for texture: %d", err);
+ return false;
+ }
+
+ m_texSize = img.size();
+
+ return true;
+}
+
+bool VulkanRenderer2::createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
+ VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
+{
+ VkDevice dev = m_window->device();
+
+ VkImageCreateInfo imageInfo;
+ memset(&imageInfo, 0, sizeof(imageInfo));
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.format = m_texFormat;
+ imageInfo.extent.width = size.width();
+ imageInfo.extent.height = size.height();
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.tiling = tiling;
+ imageInfo.usage = usage;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+
+ VkResult err = m_devFuncs->vkCreateImage(dev, &imageInfo, nullptr, image);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create linear image for texture: %d", err);
+ return false;
+ }
+
+ VkMemoryRequirements memReq;
+ m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
+
+ if (!(memReq.memoryTypeBits & (1 << memIndex))) {
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps);
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (!(memReq.memoryTypeBits & (1 << i)))
+ continue;
+ memIndex = i;
+ }
+ }
+
+ VkMemoryAllocateInfo allocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ nullptr,
+ memReq.size,
+ memIndex
+ };
+ qDebug("allocating %u bytes for texture image", uint32_t(memReq.size));
+
+ err = m_devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, mem);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to allocate memory for linear image: %d", err);
+ return false;
+ }
+
+ err = m_devFuncs->vkBindImageMemory(dev, *image, *mem, 0);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to bind linear image memory: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+bool VulkanRenderer2::writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory)
+{
+ VkDevice dev = m_window->device();
+
+ VkImageSubresource subres = {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, // mip level
+ 0
+ };
+ VkSubresourceLayout layout;
+ m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout);
+
+ uchar *p;
+ VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast(&p));
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map memory for linear image: %d", err);
+ return false;
+ }
+
+ for (int y = 0; y < img.height(); ++y) {
+ const uchar *line = img.constScanLine(y);
+ memcpy(p, line, img.width() * 4);
+ p += layout.rowPitch;
+ }
+
+ m_devFuncs->vkUnmapMemory(dev, memory);
+ return true;
+}
+
+void VulkanRenderer2::ensureTexture()
+{
+ if (!m_texLayoutPending && !m_texStagingPending)
+ return;
+
+ Q_ASSERT(m_texLayoutPending != m_texStagingPending);
+ VkCommandBuffer cb = m_window->currentCommandBuffer();
+
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
+
+ if (m_texLayoutPending) {
+ m_texLayoutPending = false;
+
+ barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ barrier.image = m_texImage;
+
+ m_devFuncs->vkCmdPipelineBarrier(cb,
+ VK_PIPELINE_STAGE_HOST_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &barrier);
+
+ VkDevice dev = m_window->device();
+
+ VkImageSubresource subres = {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, // mip level
+ 0
+ };
+ VkSubresourceLayout layout;
+ m_devFuncs->vkGetImageSubresourceLayout(dev, m_texImage, &subres, &layout);
+
+ VkResult err = m_devFuncs->vkMapMemory(dev, m_texMem, layout.offset, layout.size, 0, reinterpret_cast(&mappedPtr));
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map memory for linear image: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+ imagePitch = layout.rowPitch;
+
+ if (qobject_cast(m_window)) {
+ emit qobject_cast(m_window)->rendererInitialized();
+ }
+ } else {
+ m_texStagingPending = false;
+
+ if (!m_texStagingTransferLayout) {
+ barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ barrier.image = m_texStaging;
+ m_devFuncs->vkCmdPipelineBarrier(cb,
+ VK_PIPELINE_STAGE_HOST_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &barrier);
+
+ barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.image = m_texImage;
+ m_devFuncs->vkCmdPipelineBarrier(cb,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &barrier);
+
+ VkDevice dev = m_window->device();
+
+ VkImageSubresource subres = {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, // mip level
+ 0
+ };
+ VkSubresourceLayout layout;
+ m_devFuncs->vkGetImageSubresourceLayout(dev, m_texStaging, &subres, &layout);
+
+ VkResult err = m_devFuncs->vkMapMemory(dev, m_texStagingMem, layout.offset, layout.size, 0, reinterpret_cast(&mappedPtr));
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map memory for linear image: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+ imagePitch = layout.rowPitch;
+
+ if (qobject_cast(m_window)) {
+ emit qobject_cast(m_window)->rendererInitialized();
+ }
+
+ m_texStagingTransferLayout = true;
+ }
+
+ VkImageCopy copyInfo;
+ memset(©Info, 0, sizeof(copyInfo));
+ copyInfo.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyInfo.srcSubresource.layerCount = 1;
+ copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyInfo.dstSubresource.layerCount = 1;
+ copyInfo.extent.width = m_texSize.width();
+ copyInfo.extent.height = m_texSize.height();
+ copyInfo.extent.depth = 1;
+ m_devFuncs->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info);
+
+ 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;
+ barrier.image = m_texImage;
+ m_devFuncs->vkCmdPipelineBarrier(cb,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &barrier);
+ }
+}
+
+void VulkanRenderer2::updateSamplers()
+{
+ static int cur_video_filter_method = -1;
+
+ if (cur_video_filter_method != video_filter_method) {
+ cur_video_filter_method = video_filter_method;
+ m_devFuncs->vkDeviceWaitIdle(m_window->device());
+
+ VkDescriptorImageInfo descImageInfo = {
+ cur_video_filter_method == 1 ? m_linearSampler : m_sampler,
+ m_texView,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ };
+
+ for (int i = 0; i < m_window->concurrentFrameCount(); i++) {
+ VkWriteDescriptorSet descWrite[2];
+ memset(descWrite, 0, sizeof(descWrite));
+ descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[0].dstSet = m_descSet[i];
+ descWrite[0].dstBinding = 0;
+ descWrite[0].descriptorCount = 1;
+ descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descWrite[0].pBufferInfo = &m_uniformBufInfo[i];
+
+ descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[1].dstSet = m_descSet[i];
+ descWrite[1].dstBinding = 1;
+ descWrite[1].descriptorCount = 1;
+ descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descWrite[1].pImageInfo = &descImageInfo;
+ m_devFuncs->vkUpdateDescriptorSets(m_window->device(), 2, descWrite, 0, nullptr);
+ }
+ }
+}
+
+void VulkanRenderer2::initResources()
+{
+ qDebug("initResources");
+
+ VkDevice dev = m_window->device();
+ m_devFuncs = m_window->vulkanInstance()->deviceFunctions(dev);
+
+ // The setup is similar to hellovulkantriangle. The difference is the
+ // presence of a second vertex attribute (texcoord), a sampler, and that we
+ // need blending.
+
+ const int concurrentFrameCount = m_window->concurrentFrameCount();
+ const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
+ const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
+ qDebug("uniform buffer offset alignment is %u", (uint) uniAlign);
+ VkBufferCreateInfo bufInfo;
+ memset(&bufInfo, 0, sizeof(bufInfo));
+ bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ // Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign.
+ const VkDeviceSize vertexAllocSize = aligned(sizeof(vertexData), uniAlign);
+ const VkDeviceSize uniformAllocSize = aligned(UNIFORM_DATA_SIZE, uniAlign);
+ bufInfo.size = vertexAllocSize + concurrentFrameCount * uniformAllocSize;
+ bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+
+ VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create buffer: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ VkMemoryRequirements memReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq);
+
+ VkMemoryAllocateInfo memAllocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ nullptr,
+ memReq.size,
+ m_window->hostVisibleMemoryIndex()
+ };
+
+ err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to allocate memory: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to bind buffer memory: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ quint8 *p;
+ err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast(&p));
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map memory: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+ memcpy(p, vertexData, sizeof(vertexData));
+ QMatrix4x4 ident;
+ memset(m_uniformBufInfo, 0, sizeof(m_uniformBufInfo));
+ for (int i = 0; i < concurrentFrameCount; ++i) {
+ const VkDeviceSize offset = vertexAllocSize + i * uniformAllocSize;
+ memcpy(p + offset, ident.constData(), 16 * sizeof(float));
+ m_uniformBufInfo[i].buffer = m_buf;
+ m_uniformBufInfo[i].offset = offset;
+ m_uniformBufInfo[i].range = uniformAllocSize;
+ }
+ m_devFuncs->vkUnmapMemory(dev, m_bufMem);
+
+ VkVertexInputBindingDescription vertexBindingDesc = {
+ 0, // binding
+ 5 * sizeof(float),
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+ VkVertexInputAttributeDescription vertexAttrDesc[] = {
+ { // position
+ 0, // location
+ 0, // binding
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 0
+ },
+ { // texcoord
+ 1,
+ 0,
+ VK_FORMAT_R32G32_SFLOAT,
+ 3 * sizeof(float)
+ }
+ };
+
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo;
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.pNext = nullptr;
+ vertexInputInfo.flags = 0;
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc;
+ vertexInputInfo.vertexAttributeDescriptionCount = 2;
+ vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
+
+ // Sampler.
+ VkSamplerCreateInfo samplerInfo;
+ memset(&samplerInfo, 0, sizeof(samplerInfo));
+ samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerInfo.magFilter = VK_FILTER_NEAREST;
+ samplerInfo.minFilter = VK_FILTER_NEAREST;
+ samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.maxAnisotropy = 1.0f;
+ samplerInfo.maxLod = 0.25;
+ err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_sampler);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create sampler: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ samplerInfo.magFilter = VK_FILTER_LINEAR;
+ samplerInfo.minFilter = VK_FILTER_LINEAR;
+ err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_linearSampler);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create sampler: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ // Texture.
+ if (!createTexture()) {
+ qWarning("Failed to create texture");
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ // Set up descriptor set and its layout.
+ VkDescriptorPoolSize descPoolSizes[2] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) },
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(concurrentFrameCount) }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ descPoolInfo.maxSets = concurrentFrameCount;
+ descPoolInfo.poolSizeCount = 2;
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create descriptor pool: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ VkDescriptorSetLayoutBinding layoutBinding[2] =
+ {
+ {
+ 0, // binding
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ 1, // descriptorCount
+ VK_SHADER_STAGE_VERTEX_BIT,
+ nullptr
+ },
+ {
+ 1, // binding
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ 1, // descriptorCount
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ nullptr
+ }
+ };
+ VkDescriptorSetLayoutCreateInfo descLayoutInfo = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ nullptr,
+ 0,
+ 2, // bindingCount
+ layoutBinding
+ };
+ err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create descriptor set layout: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ for (int i = 0; i < concurrentFrameCount; ++i) {
+ VkDescriptorSetAllocateInfo descSetAllocInfo = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ nullptr,
+ m_descPool,
+ 1,
+ &m_descSetLayout
+ };
+ err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to allocate descriptor set: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ VkWriteDescriptorSet descWrite[2];
+ memset(descWrite, 0, sizeof(descWrite));
+ descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[0].dstSet = m_descSet[i];
+ descWrite[0].dstBinding = 0;
+ descWrite[0].descriptorCount = 1;
+ descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descWrite[0].pBufferInfo = &m_uniformBufInfo[i];
+
+ VkDescriptorImageInfo descImageInfo = {
+ video_filter_method == 1 ? m_linearSampler : m_sampler,
+ m_texView,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ };
+
+ descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[1].dstSet = m_descSet[i];
+ descWrite[1].dstBinding = 1;
+ descWrite[1].descriptorCount = 1;
+ descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descWrite[1].pImageInfo = &descImageInfo;
+
+ m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
+ }
+
+ // Pipeline cache
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline cache: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ // Pipeline layout
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &m_descSetLayout;
+ err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline layout: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ // Shaders
+/*
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_texcoord = texcoord;
+ gl_Position = ubuf.mvp * position;
+}
+*/
+ VkShaderModule vertShaderModule = createShader(QStringLiteral(":/texture_vert.spv"));
+/*
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D tex;
+
+void main()
+{
+ fragColor = texture(tex, v_texcoord);
+}
+*/
+ VkShaderModule fragShaderModule = createShader(QStringLiteral(":/texture_frag.spv"));
+
+ // Graphics pipeline
+ VkGraphicsPipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+ VkPipelineShaderStageCreateInfo shaderStages[2] = {
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_SHADER_STAGE_VERTEX_BIT,
+ vertShaderModule,
+ "main",
+ nullptr
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ fragShaderModule,
+ "main",
+ nullptr
+ }
+ };
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = shaderStages;
+
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+
+ VkPipelineInputAssemblyStateCreateInfo ia;
+ memset(&ia, 0, sizeof(ia));
+ ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ pipelineInfo.pInputAssemblyState = &ia;
+
+ // The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
+ // This way the pipeline does not need to be touched when resizing the window.
+ VkPipelineViewportStateCreateInfo vp;
+ memset(&vp, 0, sizeof(vp));
+ vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ vp.viewportCount = 1;
+ vp.scissorCount = 1;
+ pipelineInfo.pViewportState = &vp;
+
+ VkPipelineRasterizationStateCreateInfo rs;
+ memset(&rs, 0, sizeof(rs));
+ rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rs.polygonMode = VK_POLYGON_MODE_FILL;
+ rs.cullMode = VK_CULL_MODE_BACK_BIT;
+ rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ rs.lineWidth = 1.0f;
+ pipelineInfo.pRasterizationState = &rs;
+
+ VkPipelineMultisampleStateCreateInfo ms;
+ memset(&ms, 0, sizeof(ms));
+ ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ pipelineInfo.pMultisampleState = &ms;
+
+ VkPipelineDepthStencilStateCreateInfo ds;
+ memset(&ds, 0, sizeof(ds));
+ ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ ds.depthTestEnable = VK_TRUE;
+ ds.depthWriteEnable = VK_TRUE;
+ ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ pipelineInfo.pDepthStencilState = &ds;
+
+ VkPipelineColorBlendStateCreateInfo cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ // assume pre-multiplied alpha, blend, write out all of rgba
+ VkPipelineColorBlendAttachmentState att;
+ memset(&att, 0, sizeof(att));
+ att.colorWriteMask = 0xF;
+ att.blendEnable = VK_TRUE;
+ att.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
+ att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ att.colorBlendOp = VK_BLEND_OP_ADD;
+ att.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ att.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ att.alphaBlendOp = VK_BLEND_OP_ADD;
+ cb.attachmentCount = 1;
+ cb.pAttachments = &att;
+ pipelineInfo.pColorBlendState = &cb;
+
+ VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dyn;
+ memset(&dyn, 0, sizeof(dyn));
+ dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState);
+ dyn.pDynamicStates = dynEnable;
+ pipelineInfo.pDynamicState = &dyn;
+
+ pipelineInfo.layout = m_pipelineLayout;
+ pipelineInfo.renderPass = m_window->defaultRenderPass();
+
+ err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create graphics pipeline: %d", err);
+ return emit qobject_cast(m_window)->errorInitializing();
+ }
+
+ if (vertShaderModule)
+ m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr);
+ if (fragShaderModule)
+ m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr);
+
+ pclog("Vulkan device: %s\n", m_window->physicalDeviceProperties()->deviceName);
+ pclog("Vulkan API version: %d.%d.%d\n", VK_VERSION_MAJOR(m_window->physicalDeviceProperties()->apiVersion), VK_VERSION_MINOR(m_window->physicalDeviceProperties()->apiVersion), VK_VERSION_PATCH(m_window->physicalDeviceProperties()->apiVersion));
+ pclog("Vulkan driver version: %d.%d.%d\n", VK_VERSION_MAJOR(m_window->physicalDeviceProperties()->driverVersion), VK_VERSION_MINOR(m_window->physicalDeviceProperties()->driverVersion), VK_VERSION_PATCH(m_window->physicalDeviceProperties()->driverVersion));
+}
+
+void VulkanRenderer2::initSwapChainResources()
+{
+ qDebug("initSwapChainResources");
+
+ // Projection matrix
+ m_proj = m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
+}
+
+void VulkanRenderer2::releaseSwapChainResources()
+{
+ qDebug("releaseSwapChainResources");
+}
+
+void VulkanRenderer2::releaseResources()
+{
+ qDebug("releaseResources");
+
+ VkDevice dev = m_window->device();
+
+ if (m_sampler) {
+ m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr);
+ m_sampler = VK_NULL_HANDLE;
+ }
+
+ if (m_linearSampler) {
+ m_devFuncs->vkDestroySampler(dev, m_linearSampler, nullptr);
+ m_linearSampler = VK_NULL_HANDLE;
+ }
+
+ if (m_texStaging) {
+ m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr);
+ m_texStaging = VK_NULL_HANDLE;
+ }
+
+ if (m_texStagingMem) {
+ m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr);
+ m_texStagingMem = VK_NULL_HANDLE;
+ }
+
+ if (m_texView) {
+ m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr);
+ m_texView = VK_NULL_HANDLE;
+ }
+
+ if (m_texImage) {
+ m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr);
+ m_texImage = VK_NULL_HANDLE;
+ }
+
+ if (m_texMem) {
+ m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr);
+ m_texMem = VK_NULL_HANDLE;
+ }
+
+ if (m_pipeline) {
+ m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
+ m_pipeline = VK_NULL_HANDLE;
+ }
+
+ if (m_pipelineLayout) {
+ m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr);
+ m_pipelineLayout = VK_NULL_HANDLE;
+ }
+
+ if (m_pipelineCache) {
+ m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
+ m_pipelineCache = VK_NULL_HANDLE;
+ }
+
+ if (m_descSetLayout) {
+ m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr);
+ m_descSetLayout = VK_NULL_HANDLE;
+ }
+
+ if (m_descPool) {
+ m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr);
+ m_descPool = VK_NULL_HANDLE;
+ }
+
+ if (m_buf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr);
+ m_buf = VK_NULL_HANDLE;
+ }
+
+ if (m_bufMem) {
+ m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
+ m_bufMem = VK_NULL_HANDLE;
+ }
+}
+
+void VulkanRenderer2::startNextFrame()
+{
+ VkDevice dev = m_window->device();
+ VkCommandBuffer cb = m_window->currentCommandBuffer();
+ const QSize sz = m_window->swapChainImageSize();
+
+ updateSamplers();
+ // Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done.
+ ensureTexture();
+
+ VkClearColorValue clearColor = {{ 0, 0, 0, 1 }};
+ VkClearDepthStencilValue clearDS = { 1, 0 };
+ VkClearValue clearValues[2];
+ memset(clearValues, 0, sizeof(clearValues));
+ clearValues[0].color = clearColor;
+ clearValues[1].depthStencil = clearDS;
+
+ VkRenderPassBeginInfo rpBeginInfo;
+ memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
+ rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ rpBeginInfo.renderPass = m_window->defaultRenderPass();
+ rpBeginInfo.framebuffer = m_window->currentFramebuffer();
+ rpBeginInfo.renderArea.extent.width = sz.width();
+ rpBeginInfo.renderArea.extent.height = sz.height();
+ rpBeginInfo.clearValueCount = 2;
+ rpBeginInfo.pClearValues = clearValues;
+ VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
+ m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ quint8 *p;
+ VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset,
+ UNIFORM_DATA_SIZE, 0, reinterpret_cast(&p));
+ if (err != VK_SUCCESS)
+ qFatal("Failed to map memory: %d", err);
+ QMatrix4x4 m = m_proj;
+ //m.rotate(m_rotation, 0, 0, 1);
+ memcpy(p, m.constData(), 16 * sizeof(float));
+ m_devFuncs->vkUnmapMemory(dev, m_bufMem);
+ p = nullptr;
+
+ // Second pass for texture coordinates.
+ err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0,
+ sizeof(vertexData), 0, reinterpret_cast(&p));
+ if (err != VK_SUCCESS)
+ qFatal("Failed to map memory: %d", err);
+
+ float* floatData = (float*)p;
+ auto source = qobject_cast(m_window)->source;
+ auto destination = qobject_cast(m_window)->destination;
+ floatData[3] = (float)source.x() / 2048.f;
+ floatData[9] = (float)(source.y()) / 2048.f;
+ floatData[8] = (float)source.x() / 2048.f;
+ floatData[4] = (float)(source.y() + source.height()) / 2048.f;
+ floatData[13] = (float)(source.x() + source.width()) / 2048.f;
+ floatData[19] = (float)(source.y()) / 2048.f;
+ floatData[18] = (float)(source.x() + source.width()) / 2048.f;
+ floatData[14] = (float)(source.y() + source.height()) / 2048.f;
+
+ m_devFuncs->vkUnmapMemory(dev, m_bufMem);
+
+ m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
+ m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
+ &m_descSet[m_window->currentFrame()], 0, nullptr);
+ VkDeviceSize vbOffset = 0;
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
+
+ VkViewport viewport;
+ viewport.x = destination.x();
+ viewport.y = destination.y();
+ viewport.width = destination.width();
+ viewport.height = destination.height();
+ viewport.minDepth = 0;
+ viewport.maxDepth = 1;
+ m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
+
+ VkRect2D scissor;
+ scissor.offset.x = viewport.x;
+ scissor.offset.y = viewport.y;
+ scissor.extent.width = viewport.width;
+ scissor.extent.height = viewport.height;
+ m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
+
+ m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
+
+ m_devFuncs->vkCmdEndRenderPass(cmdBuf);
+
+ if (m_texStagingTransferLayout) {
+ VkImageMemoryBarrier barrier{};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ barrier.image = m_texImage;
+ m_devFuncs->vkCmdPipelineBarrier(cb,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &barrier);
+ m_texStagingPending = true;
+ }
+
+ m_window->frameReady();
+ m_window->requestUpdate(); // render continuously, throttled by the presentation rate
+}
+#endif
diff --git a/src/qt/qt_vulkanrenderer.hpp b/src/qt/qt_vulkanrenderer.hpp
new file mode 100644
index 000000000..abd4d7a13
--- /dev/null
+++ b/src/qt/qt_vulkanrenderer.hpp
@@ -0,0 +1,95 @@
+#pragma once
+/****************************************************************************
+**
+** Copyright (C) 2022 Cacodemon345
+** Copyright (C) 2017 The Qt Company Ltd.
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+#include
+#include
+
+#if QT_CONFIG(vulkan)
+#include "qt_vulkanwindowrenderer.hpp"
+
+class VulkanRenderer2 : public QVulkanWindowRenderer
+{
+public:
+ void* mappedPtr = nullptr;
+ size_t imagePitch = 2048 * 4;
+ VulkanRenderer2(QVulkanWindow *w);
+
+ void initResources() override;
+ void initSwapChainResources() override;
+ void releaseSwapChainResources() override;
+ void releaseResources() override;
+
+ void startNextFrame() override;
+
+private:
+ VkShaderModule createShader(const QString &name);
+ bool createTexture();
+ bool createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
+ VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex);
+ bool writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory);
+ void ensureTexture();
+ void updateSamplers();
+
+ QVulkanWindow *m_window;
+ QVulkanDeviceFunctions *m_devFuncs;
+
+ VkDeviceMemory m_bufMem = VK_NULL_HANDLE;
+ VkBuffer m_buf = VK_NULL_HANDLE;
+ VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
+
+ VkDescriptorPool m_descPool = VK_NULL_HANDLE;
+ VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
+ VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
+
+ VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+
+ VkSampler m_sampler = VK_NULL_HANDLE;
+ VkSampler m_linearSampler = VK_NULL_HANDLE;
+ VkImage m_texImage = VK_NULL_HANDLE;
+ VkDeviceMemory m_texMem = VK_NULL_HANDLE;
+ bool m_texLayoutPending = false;
+ VkImageView m_texView = VK_NULL_HANDLE;
+ VkImage m_texStaging = VK_NULL_HANDLE;
+ VkDeviceMemory m_texStagingMem = VK_NULL_HANDLE;
+ bool m_texStagingPending = false;
+ bool m_texStagingTransferLayout = false;
+ QSize m_texSize;
+ VkFormat m_texFormat;
+
+ QMatrix4x4 m_proj;
+ float m_rotation = 0.0f;
+};
+#endif
diff --git a/src/qt/qt_vulkanwindowrenderer.cpp b/src/qt/qt_vulkanwindowrenderer.cpp
new file mode 100644
index 000000000..4563bd349
--- /dev/null
+++ b/src/qt/qt_vulkanwindowrenderer.cpp
@@ -0,0 +1,851 @@
+#include "qt_vulkanwindowrenderer.hpp"
+
+#include
+#include
+#include
+#include
+
+#if QT_CONFIG(vulkan)
+#include
+#include
+
+#include "qt_mainwindow.hpp"
+#include "qt_vulkanrenderer.hpp"
+
+extern "C"
+{
+#include <86box/86box.h>
+#include <86box/video.h>
+}
+#if 0
+extern MainWindow* main_window;
+/*
+#version 450
+
+layout(location = 0) out vec2 v_texcoord;
+
+mat4 mvp = mat4(
+vec4(1.0, 0, 0, 0),
+vec4(0, 1.0, 0, 0),
+vec4(0, 0, 1.0, 0),
+vec4(0, 0, 0, 1.0)
+);
+
+// Y coordinate in Vulkan viewport space is flipped as compared to OpenGL.
+vec4 vertCoords[4] = vec4[](
+vec4(-1.0, -1.0, 0, 1),
+vec4(1.0, -1.0, 0, 1),
+vec4(-1.0, 1.0, 0, 1),
+vec4(1.0, 1.0, 0, 1)
+);
+
+layout(push_constant) uniform texCoordBuf {
+ vec2 texCoords[4];
+} texCoordUniform;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_texcoord = texCoordUniform.texCoords[gl_VertexIndex];
+ gl_Position = mvp * vertCoords[gl_VertexIndex];
+}
+*/
+// Compiled with glslangValidator -V
+static const uint8_t vertShaderCode[] = {
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00,
+ 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x6d, 0x76, 0x70, 0x00, 0x05, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x00, 0x00,
+ 0x05, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65,
+ 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
+ 0x42, 0x75, 0x66, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
+ 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x55, 0x6e, 0x69, 0x66,
+ 0x6f, 0x72, 0x6d, 0x00, 0x05, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x67, 0x6c, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x64,
+ 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+ 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf,
+ 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x3d, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x3d, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
+ 0x38, 0x00, 0x01, 0x00
+};
+
+/*
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D tex;
+
+void main()
+{
+ fragColor = texture(tex, v_texcoord);
+}
+*/
+
+static const uint8_t fragShaderCode[]
+{
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67,
+ 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f,
+ 0x72, 0x64, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x3f, 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x3d, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
+ 0x38, 0x00, 0x01, 0x00
+};
+
+class VulkanRendererEmu : public QVulkanWindowRenderer
+{
+ VulkanWindowRenderer* m_window{nullptr};
+ QVulkanDeviceFunctions* m_devFuncs{nullptr};
+ VmaAllocator allocator{nullptr};
+ VmaAllocation allocation{nullptr};
+ VkImage image{nullptr};
+ VkImageView imageView{nullptr};
+ VkPipeline pipeline{nullptr};
+ VkPipelineLayout pipelineLayout{nullptr};
+ VkDescriptorSetLayout descLayout{nullptr};
+ VkDescriptorPool descPool{nullptr};
+ VkDescriptorSet descSet{nullptr};
+ VkSampler sampler{nullptr}, nearestSampler{nullptr};
+ bool imageLayoutTransitioned{false};
+ int cur_video_filter_method = -1;
+private:
+ void updateOptions() {
+ VkResult res;
+ if (cur_video_filter_method == video_filter_method) return;
+ if (!descPool) {
+ VkDescriptorPoolSize descSize{};
+ descSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descSize.descriptorCount = 2;
+
+ VkDescriptorPoolCreateInfo poolInfo{};
+ poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ poolInfo.poolSizeCount = 1;
+ poolInfo.pPoolSizes = &descSize;
+ poolInfo.maxSets = 2;
+
+ if ((res = m_devFuncs->vkCreateDescriptorPool(m_window->device(), &poolInfo, nullptr, &descPool)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create descriptor pool. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ VkDescriptorSetAllocateInfo allocInfo{};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descPool;
+ allocInfo.descriptorSetCount = 1;
+ allocInfo.pSetLayouts = &descLayout;
+
+ if ((res = m_devFuncs->vkAllocateDescriptorSets(m_window->device(), &allocInfo, &descSet)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create descriptor set. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }}
+ VkDescriptorImageInfo imageDescInfo{};
+ imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ imageDescInfo.imageView = imageView;
+ imageDescInfo.sampler = video_filter_method == 0 ? nearestSampler : sampler;
+
+ VkWriteDescriptorSet descWrite{};
+ descWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite.dstSet = descSet;
+ descWrite.dstBinding = 1;
+ descWrite.dstArrayElement = 0;
+ descWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descWrite.descriptorCount = 1;
+ descWrite.pImageInfo = &imageDescInfo;
+
+ m_devFuncs->vkUpdateDescriptorSets(m_window->device(), 1, &descWrite, 0, nullptr);
+ cur_video_filter_method = video_filter_method;
+ }
+public:
+ void* mappedPtr = nullptr;
+ uint32_t imagePitch{2048 * 4};
+ VulkanRendererEmu(VulkanWindowRenderer *w) : m_window(w), m_devFuncs(nullptr) { }
+
+ void initResources() override
+ {
+ VmaAllocatorCreateInfo info{};
+ info.instance = m_window->vulkanInstance()->vkInstance();
+ info.device = m_window->device();
+ info.physicalDevice = m_window->physicalDevice();
+ VmaVulkanFunctions funcs{};
+ funcs.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)m_window->vulkanInstance()->getInstanceProcAddr("vkGetInstanceProcAddr");
+ funcs.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)m_window->vulkanInstance()->getInstanceProcAddr("vkGetDeviceProcAddr");
+ info.pVulkanFunctions = &funcs;
+ if (vmaCreateAllocator(&info, &allocator) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create Vulkan allocator. Switch to another renderer");
+ return;
+ }
+
+ m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
+
+ VkResult res;
+ VkImageCreateInfo imageInfo{};
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
+ imageInfo.extent.width = imageInfo.extent.height = 2048;
+ imageInfo.extent.depth = imageInfo.arrayLayers = imageInfo.mipLevels = 1;
+ imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
+ imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+
+ VmaAllocationInfo allocatedInfo{};
+ VmaAllocationCreateInfo allocInfo{};
+ allocInfo.pUserData = allocInfo.pool = nullptr;
+ allocInfo.requiredFlags = allocInfo.preferredFlags = 0;
+ allocInfo.usage = VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO;
+ allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
+ VMA_ALLOCATION_CREATE_MAPPED_BIT;
+
+ if ((res = vmaCreateImage(allocator, &imageInfo, &allocInfo, &image, &allocation, &allocatedInfo)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create Vulkan image. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ };
+
+ VkImageViewCreateInfo imageViewInfo{};
+ imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imageViewInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
+ imageViewInfo.image = image;
+ imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageViewInfo.subresourceRange.baseMipLevel = 0;
+ imageViewInfo.subresourceRange.levelCount = 1;
+ imageViewInfo.subresourceRange.baseArrayLayer = 0;
+ imageViewInfo.subresourceRange.layerCount = 1;
+ imageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ imageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ imageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ imageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+
+ if ((res = m_devFuncs->vkCreateImageView(m_window->device(), &imageViewInfo, nullptr, &imageView)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create Vulkan image view. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ // Begin Pipeline creation.
+ {
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 0;
+ vertexInputInfo.pVertexBindingDescriptions = nullptr;
+ vertexInputInfo.vertexAttributeDescriptionCount = 0;
+ vertexInputInfo.pVertexAttributeDescriptions = nullptr;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
+ inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ inputAssembly.primitiveRestartEnable = VK_FALSE;
+
+ VkRect2D scissor{};
+ scissor.offset = {0, 0};
+ scissor.extent = {2048, 2048};
+
+ VkViewport viewport{};
+ viewport.x = m_window->destination.x();
+ viewport.y = m_window->destination.y();
+ viewport.width = (float)m_window->destination.width();
+ viewport.height = (float)m_window->destination.height();
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkPipelineViewportStateCreateInfo viewportState{};
+ viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportState.viewportCount = 1;
+ viewportState.pViewports = &viewport;
+ viewportState.scissorCount = 1;
+ viewportState.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizer{};
+ rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizer.depthClampEnable = VK_FALSE;
+ rasterizer.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizer.lineWidth = 1.0f;
+ rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+ rasterizer.depthBiasConstantFactor = 0.0f;
+ rasterizer.depthBiasClamp = 0.0f;
+ rasterizer.depthBiasSlopeFactor = 0.0f;
+
+ VkPipelineMultisampleStateCreateInfo multisampling{};
+ multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampling.sampleShadingEnable = VK_FALSE;
+ multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ multisampling.minSampleShading = 1.0f;
+ multisampling.pSampleMask = nullptr;
+ multisampling.alphaToCoverageEnable = VK_FALSE;
+ multisampling.alphaToOneEnable = VK_FALSE;
+
+ VkPipelineColorBlendAttachmentState colorBlendAttachment{};
+ colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.blendEnable = VK_TRUE;
+ colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
+ colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
+ colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
+
+ VkPipelineColorBlendStateCreateInfo colorBlending{};
+ colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlending.logicOpEnable = VK_FALSE;
+ colorBlending.logicOp = VK_LOGIC_OP_COPY;
+ colorBlending.attachmentCount = 1;
+ colorBlending.pAttachments = &colorBlendAttachment;
+ colorBlending.blendConstants[0] = 0.0f;
+ colorBlending.blendConstants[1] = 0.0f;
+ colorBlending.blendConstants[2] = 0.0f;
+ colorBlending.blendConstants[3] = 0.0f;
+
+ VkDynamicState dynState = VK_DYNAMIC_STATE_VIEWPORT;
+ VkPipelineDynamicStateCreateInfo dynamicState{};
+ dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicState.dynamicStateCount = 1;
+ dynamicState.pDynamicStates = &dynState;
+
+ VkPushConstantRange range{};
+ range.offset = 0;
+ range.size = sizeof(float) * 8;
+ range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+
+ // Sampler binding start.
+ VkDescriptorSetLayoutBinding samplerLayoutBinding{};
+ samplerLayoutBinding.binding = 1;
+ samplerLayoutBinding.descriptorCount = 1;
+ samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ samplerLayoutBinding.pImmutableSamplers = nullptr;
+ samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+ VkDescriptorSetLayoutCreateInfo layoutInfo{};
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = 1;
+ layoutInfo.pBindings = &samplerLayoutBinding;
+
+ if ((res = m_devFuncs->vkCreateDescriptorSetLayout(m_window->device(), &layoutInfo, nullptr, &descLayout))) {
+ QMessageBox::critical(main_window, "86Box", "Could not create descriptor set layout. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+ // Sampler binding end.
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &descLayout;
+ pipelineLayoutInfo.pushConstantRangeCount = 1;
+ pipelineLayoutInfo.pPushConstantRanges = ⦥
+
+ if ((res = m_devFuncs->vkCreatePipelineLayout(m_window->device(), &pipelineLayoutInfo, nullptr, &pipelineLayout)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create pipeline layout. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ // Shader loading start.
+ VkShaderModuleCreateInfo createInfo{};
+ createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ createInfo.codeSize = sizeof(vertShaderCode);
+ createInfo.pCode = (uint32_t*)vertShaderCode;
+ VkShaderModule vertModule{nullptr}, fragModule{nullptr};
+ if ((res = m_devFuncs->vkCreateShaderModule(m_window->device(), &createInfo, nullptr, &vertModule)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create vertex shader. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ createInfo.codeSize = sizeof(fragShaderCode);
+ createInfo.pCode = (uint32_t*)fragShaderCode;
+ if ((res = m_devFuncs->vkCreateShaderModule(m_window->device(), &createInfo, nullptr, &fragModule)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create fragment shader. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
+ vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
+ vertShaderStageInfo.module = vertModule;
+ vertShaderStageInfo.pName = "main";
+
+ VkPipelineShaderStageCreateInfo fragShaderStageInfo{vertShaderStageInfo};
+ fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ fragShaderStageInfo.module = fragModule;
+
+ VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
+ // Shader loading end.
+
+ VkPipelineDepthStencilStateCreateInfo depthInfo{};
+ depthInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ depthInfo.pNext = nullptr;
+ depthInfo.depthTestEnable = VK_TRUE;
+ depthInfo.depthWriteEnable = VK_TRUE;
+ depthInfo.depthBoundsTestEnable = VK_FALSE;
+ depthInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ depthInfo.stencilTestEnable = VK_FALSE;
+ depthInfo.maxDepthBounds = 1.0f;
+
+ VkGraphicsPipelineCreateInfo pipelineInfo{};
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = shaderStages;
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+ pipelineInfo.pInputAssemblyState = &inputAssembly;
+ pipelineInfo.pViewportState = &viewportState;
+ pipelineInfo.pRasterizationState = &rasterizer;
+ pipelineInfo.pMultisampleState = &multisampling;
+ pipelineInfo.pDepthStencilState = &depthInfo;
+ pipelineInfo.pColorBlendState = &colorBlending;
+ pipelineInfo.pDynamicState = &dynamicState;
+ pipelineInfo.layout = pipelineLayout;
+ pipelineInfo.renderPass = m_window->defaultRenderPass();
+ pipelineInfo.subpass = 0;
+
+ if ((res = m_devFuncs->vkCreateGraphicsPipelines(m_window->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline)) != VK_SUCCESS) {
+ m_devFuncs->vkDestroyShaderModule(m_window->device(), vertModule, nullptr);
+ m_devFuncs->vkDestroyShaderModule(m_window->device(), fragModule, nullptr);
+ QMessageBox::critical(main_window, "86Box", "Could not create graphics pipeline. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+ m_devFuncs->vkDestroyShaderModule(m_window->device(), vertModule, nullptr);
+ m_devFuncs->vkDestroyShaderModule(m_window->device(), fragModule, nullptr);
+ }
+
+ VkSamplerCreateInfo samplerInfo{};
+ samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerInfo.magFilter = VK_FILTER_LINEAR;
+ samplerInfo.minFilter = VK_FILTER_LINEAR;
+ samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerInfo.anisotropyEnable = VK_FALSE;
+ samplerInfo.unnormalizedCoordinates = VK_FALSE;
+ samplerInfo.compareEnable = VK_FALSE;
+ samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
+ samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ samplerInfo.mipLodBias = 0.0f;
+ samplerInfo.minLod = 0.0f;
+ samplerInfo.maxLod = 0.0;
+
+ if ((res = m_devFuncs->vkCreateSampler(m_window->device(), &samplerInfo, nullptr, &sampler)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create linear image sampler. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ samplerInfo.magFilter = samplerInfo.minFilter = VK_FILTER_NEAREST;
+ samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
+
+ if ((res = m_devFuncs->vkCreateSampler(m_window->device(), &samplerInfo, nullptr, &nearestSampler)) != VK_SUCCESS) {
+ QMessageBox::critical(main_window, "86Box", "Could not create nearest image sampler. Switch to another renderer. " + Vulkan_GetResultString(res));
+ return;
+ }
+
+ updateOptions();
+
+ VkImageSubresource resource{};
+ resource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ resource.arrayLayer = 0;
+ resource.mipLevel = 0;
+ VkSubresourceLayout layout{};
+ m_devFuncs->vkGetImageSubresourceLayout(m_window->device(), image, &resource, &layout);
+
+ imagePitch = layout.rowPitch;
+ mappedPtr = (uint8_t*)allocatedInfo.pMappedData + layout.offset;
+ emit m_window->rendererInitialized();
+ }
+
+ void releaseResources() override
+ {
+ if (pipeline) m_devFuncs->vkDestroyPipeline(m_window->device(), pipeline, nullptr);
+ if (pipelineLayout) m_devFuncs->vkDestroyPipelineLayout(m_window->device(), pipelineLayout, nullptr);
+ if (descSet) m_devFuncs->vkDestroyDescriptorPool(m_window->device(), descPool, nullptr);
+ if (descLayout) m_devFuncs->vkDestroyDescriptorSetLayout(m_window->device(), descLayout, nullptr);
+ if (imageView) m_devFuncs->vkDestroyImageView(m_window->device(), imageView, nullptr);
+ if (image) vmaDestroyImage(allocator, image, allocation);
+ if (sampler) m_devFuncs->vkDestroySampler(m_window->device(), sampler, nullptr);
+ if (nearestSampler) m_devFuncs->vkDestroySampler(m_window->device(), nearestSampler, nullptr);
+ image = nullptr;
+ pipeline = nullptr;
+ pipelineLayout = nullptr;
+ imageView = nullptr;
+ descLayout = nullptr;
+ descPool = nullptr;
+ descSet = nullptr;
+ sampler = nullptr;
+ vmaDestroyAllocator(allocator);
+ allocator = nullptr;
+ }
+
+ QString Vulkan_GetResultString(VkResult result)
+ {
+ switch ((int)result) {
+ case VK_SUCCESS:
+ return "VK_SUCCESS";
+ case VK_NOT_READY:
+ return "VK_NOT_READY";
+ case VK_TIMEOUT:
+ return "VK_TIMEOUT";
+ case VK_EVENT_SET:
+ return "VK_EVENT_SET";
+ case VK_EVENT_RESET:
+ return "VK_EVENT_RESET";
+ case VK_INCOMPLETE:
+ return "VK_INCOMPLETE";
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ return "VK_ERROR_OUT_OF_HOST_MEMORY";
+ case VK_ERROR_OUT_OF_DEVICE_MEMORY:
+ return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
+ case VK_ERROR_INITIALIZATION_FAILED:
+ return "VK_ERROR_INITIALIZATION_FAILED";
+ case VK_ERROR_DEVICE_LOST:
+ return "VK_ERROR_DEVICE_LOST";
+ case VK_ERROR_MEMORY_MAP_FAILED:
+ return "VK_ERROR_MEMORY_MAP_FAILED";
+ case VK_ERROR_LAYER_NOT_PRESENT:
+ return "VK_ERROR_LAYER_NOT_PRESENT";
+ case VK_ERROR_EXTENSION_NOT_PRESENT:
+ return "VK_ERROR_EXTENSION_NOT_PRESENT";
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ return "VK_ERROR_FEATURE_NOT_PRESENT";
+ case VK_ERROR_INCOMPATIBLE_DRIVER:
+ return "VK_ERROR_INCOMPATIBLE_DRIVER";
+ case VK_ERROR_TOO_MANY_OBJECTS:
+ return "VK_ERROR_TOO_MANY_OBJECTS";
+ case VK_ERROR_FORMAT_NOT_SUPPORTED:
+ return "VK_ERROR_FORMAT_NOT_SUPPORTED";
+ case VK_ERROR_FRAGMENTED_POOL:
+ return "VK_ERROR_FRAGMENTED_POOL";
+ case VK_ERROR_UNKNOWN:
+ return "VK_ERROR_UNKNOWN";
+ case VK_ERROR_OUT_OF_POOL_MEMORY:
+ return "VK_ERROR_OUT_OF_POOL_MEMORY";
+ case VK_ERROR_INVALID_EXTERNAL_HANDLE:
+ return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
+ case VK_ERROR_FRAGMENTATION:
+ return "VK_ERROR_FRAGMENTATION";
+ case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS:
+ return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
+ case VK_ERROR_SURFACE_LOST_KHR:
+ return "VK_ERROR_SURFACE_LOST_KHR";
+ case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
+ return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
+ case VK_SUBOPTIMAL_KHR:
+ return "VK_SUBOPTIMAL_KHR";
+ case VK_ERROR_OUT_OF_DATE_KHR:
+ return "VK_ERROR_OUT_OF_DATE_KHR";
+ case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
+ return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
+ case VK_ERROR_VALIDATION_FAILED_EXT:
+ return "VK_ERROR_VALIDATION_FAILED_EXT";
+ case VK_ERROR_INVALID_SHADER_NV:
+ return "VK_ERROR_INVALID_SHADER_NV";
+#if VK_HEADER_VERSION >= 135 && VK_HEADER_VERSION < 162
+ case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
+ return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
+#endif
+ case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
+ return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
+ case VK_ERROR_NOT_PERMITTED_EXT:
+ return "VK_ERROR_NOT_PERMITTED_EXT";
+ case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
+ return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
+ case VK_THREAD_IDLE_KHR:
+ return "VK_THREAD_IDLE_KHR";
+ case VK_THREAD_DONE_KHR:
+ return "VK_THREAD_DONE_KHR";
+ case VK_OPERATION_DEFERRED_KHR:
+ return "VK_OPERATION_DEFERRED_KHR";
+ case VK_OPERATION_NOT_DEFERRED_KHR:
+ return "VK_OPERATION_NOT_DEFERRED_KHR";
+ case VK_PIPELINE_COMPILE_REQUIRED_EXT:
+ return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
+ default:
+ break;
+ }
+ if (result < 0) {
+ return "VK_ERROR_";
+ }
+ return "VK_";
+ }
+
+ void startNextFrame() override
+ {
+ m_devFuncs->vkDeviceWaitIdle(m_window->device());
+ VkClearValue values[2];
+ auto cmdBufs = m_window->currentCommandBuffer();
+ memset(values, 0, sizeof(values));
+ values[0].depthStencil = { 1, 0 };
+ values[1].depthStencil = { 1, 0 };
+ VkRenderPassBeginInfo info{};
+ VkSubpassDependency deps{};
+ info.pClearValues = values;
+ info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ info.framebuffer = m_window->currentFramebuffer();
+ info.renderArea = VkRect2D{{0, 0}, {(uint32_t)m_window->swapChainImageSize().width(), (uint32_t)m_window->swapChainImageSize().height()}};
+ info.clearValueCount = 2;
+ info.renderPass = m_window->defaultRenderPass();
+
+ updateOptions();
+ if (!imageLayoutTransitioned) {
+ VkPipelineStageFlags srcflags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_HOST_BIT, dstflags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+
+ VkImageMemoryBarrier barrier{};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.image = image;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ m_devFuncs->vkCmdPipelineBarrier(cmdBufs, srcflags, dstflags, 0, 0, nullptr, 0, nullptr, 1, &barrier);
+ imageLayoutTransitioned = true;
+ }
+ vmaFlushAllocation(allocator, allocation, 0, VK_WHOLE_SIZE);
+ VkViewport viewport{};
+ viewport.x = m_window->destination.x();
+ viewport.y = m_window->destination.y();
+ viewport.width = (float)m_window->destination.width();
+ viewport.height = (float)m_window->destination.height();
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+ m_devFuncs->vkCmdSetViewport(cmdBufs, 0, 1, &viewport);
+ m_devFuncs->vkCmdBeginRenderPass(m_window->currentCommandBuffer(), &info, VK_SUBPASS_CONTENTS_INLINE);
+ m_devFuncs->vkCmdBindPipeline(cmdBufs, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ m_devFuncs->vkCmdBindDescriptorSets(cmdBufs, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descSet, 0, nullptr);
+ std::array texcoords;
+ auto source = m_window->source;
+ texcoords[0] = (QVector2D((float)source.x() / 2048.f, (float)(source.y()) / 2048.f));
+ texcoords[2] = (QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f));
+ texcoords[1] = (QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f));
+ texcoords[3] = (QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f));
+ m_devFuncs->vkCmdPushConstants(cmdBufs, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(QVector2D) * 4, texcoords.data());
+ m_devFuncs->vkCmdDraw(cmdBufs, 4, 1, 0, 0);
+ m_devFuncs->vkCmdEndRenderPass(cmdBufs);
+
+ m_window->frameReady();
+ m_devFuncs->vkDeviceWaitIdle(m_window->device());
+ }
+};
+#endif
+
+VulkanWindowRenderer::VulkanWindowRenderer(QWidget* parent)
+ : QVulkanWindow(parent->windowHandle())
+{
+ parentWidget = parent;
+ instance.setLayers(QByteArrayList() << "VK_LAYER_KHRONOS_validation");
+ instance.setExtensions(QByteArrayList() << "VK_EXT_debug_report");
+ if (!instance.create()) {
+ throw std::runtime_error("Could not create Vulkan instance");
+ }
+ uint32_t physicalDevices = 0;
+ instance.functions()->vkEnumeratePhysicalDevices(instance.vkInstance(), &physicalDevices, nullptr);
+ if (physicalDevices == 0) {
+ throw std::runtime_error("No physical devices available.");
+ }
+ qDebug() << instance.layers();
+ setVulkanInstance(&instance);
+ setPhysicalDeviceIndex(0);
+ setFlags(Flag::PersistentResources);
+ buf_usage = std::vector(1);
+ buf_usage[0].clear();
+}
+
+QVulkanWindowRenderer* VulkanWindowRenderer::createRenderer()
+{
+ renderer = new VulkanRenderer2(this);
+ return renderer;
+}
+
+void VulkanWindowRenderer::resizeEvent(QResizeEvent *event) {
+ onResize(width(), height());
+
+ QVulkanWindow::resizeEvent(event);
+}
+
+bool VulkanWindowRenderer::event(QEvent *event)
+{
+ bool res = false;
+ if (!eventDelegate(event, res)) return QVulkanWindow::event(event);
+ return res;
+}
+
+void VulkanWindowRenderer::onBlit(int buf_idx, int x, int y, int w, int h)
+{
+ source.setRect(x, y, w, h);
+ if (isExposed()) requestUpdate();
+ buf_usage[0].clear();
+}
+
+uint32_t VulkanWindowRenderer::getBytesPerRow()
+{
+ return renderer->imagePitch;
+}
+
+std::vector> VulkanWindowRenderer::getBuffers()
+{
+ return std::vector{std::make_tuple((uint8_t*)renderer->mappedPtr, &this->buf_usage[0])};
+}
+#endif
diff --git a/src/qt/qt_vulkanwindowrenderer.hpp b/src/qt/qt_vulkanwindowrenderer.hpp
new file mode 100644
index 000000000..df252d393
--- /dev/null
+++ b/src/qt/qt_vulkanwindowrenderer.hpp
@@ -0,0 +1,39 @@
+#ifndef VULKANWINDOWRENDERER_HPP
+#define VULKANWINDOWRENDERER_HPP
+
+#include
+
+#if QT_CONFIG(vulkan)
+#include "qt_renderercommon.hpp"
+#include "qt_vulkanrenderer.hpp"
+
+class VulkanRenderer2;
+
+class VulkanWindowRenderer : public QVulkanWindow, public RendererCommon
+{
+ Q_OBJECT
+public:
+ VulkanWindowRenderer(QWidget* parent);
+public slots:
+ void onBlit(int buf_idx, int x, int y, int w, int h);
+signals:
+ void rendererInitialized();
+ void errorInitializing();
+protected:
+ virtual std::vector> getBuffers() override;
+ void resizeEvent(QResizeEvent*) override;
+ bool event(QEvent*) override;
+ uint32_t getBytesPerRow() override;
+private:
+ QVulkanInstance instance;
+
+ QVulkanWindowRenderer* createRenderer() override;
+
+ friend class VulkanRendererEmu;
+ friend class VulkanRenderer2;
+
+ VulkanRenderer2* renderer;
+};
+#endif
+
+#endif // VULKANWINDOWRENDERER_HPP
diff --git a/src/qt/texture_frag.spv b/src/qt/texture_frag.spv
new file mode 100644
index 000000000..7521ef6ee
Binary files /dev/null and b/src/qt/texture_frag.spv differ
diff --git a/src/qt/texture_vert.spv b/src/qt/texture_vert.spv
new file mode 100644
index 000000000..6292c0de3
Binary files /dev/null and b/src/qt/texture_vert.spv differ
diff --git a/src/qt_resources.qrc b/src/qt_resources.qrc
index 045c3659d..fec56ad71 100644
--- a/src/qt_resources.qrc
+++ b/src/qt_resources.qrc
@@ -57,4 +57,8 @@
win/icons/send_cae.ico
win/icons/settings.ico
+
+ qt/texture_vert.spv
+ qt/texture_frag.spv
+