From 44a2958c31bcd6fe668de8b24eb4643b5922ee14 Mon Sep 17 00:00:00 2001 From: BearOso Date: Tue, 31 Jan 2023 17:18:07 -0600 Subject: [PATCH] Windows Vulkan working generically. --- vulkan/vulkan_texture.hpp | 2 +- win32/CVulkan.cpp | 335 +++++++++++++++++++++++++++++++++++++- win32/CVulkan.h | 16 +- win32/win32_display.cpp | 5 + 4 files changed, 344 insertions(+), 14 deletions(-) diff --git a/vulkan/vulkan_texture.hpp b/vulkan/vulkan_texture.hpp index 839899e0..56d8758b 100644 --- a/vulkan/vulkan_texture.hpp +++ b/vulkan/vulkan_texture.hpp @@ -10,7 +10,7 @@ struct Texture { Texture(); void init(vk::Device device, vk::CommandPool command, vk::Queue queue, vma::Allocator allocator); - void init(Vulkan::Context *context); + void init(Context *context); ~Texture(); void create(int width, int height, vk::Format fmt, vk::SamplerAddressMode wrap_mode, bool linear, bool mipmap); diff --git a/win32/CVulkan.cpp b/win32/CVulkan.cpp index d6ca3d9f..ca70a976 100644 --- a/win32/CVulkan.cpp +++ b/win32/CVulkan.cpp @@ -1,39 +1,175 @@ #include "CVulkan.h" +#include "../vulkan/slang_shader.hpp" +#include "snes9x.h" +#include +#include -CVulkan::CVulkan() -{ -} - -CVulkan::~CVulkan() -{ -} +#include "../filter/hq2x.h" +#include "../filter/2xsai.h" bool CVulkan::Initialize(HWND hWnd) { this->hWnd = hWnd; - RECT window_rect; GetWindowRect(hWnd, &window_rect); current_width = window_rect.right - window_rect.left; current_height = window_rect.bottom - window_rect.top; context = std::make_unique(); + if (!context->init_win32(0, hWnd)) + return false; + swapchain = context->swapchain.get(); + device = context->device; + + create_pipeline(); + + descriptors.clear(); + for (size_t i = 0; i < swapchain->get_num_frames(); i++) + { + vk::DescriptorSetAllocateInfo dsai{}; + dsai + .setDescriptorPool(context->descriptor_pool.get()) + .setDescriptorSetCount(1) + .setSetLayouts(descriptor_set_layout.get()); + auto descriptor = device.allocateDescriptorSetsUnique(dsai); + descriptors.push_back(std::move(descriptor[0])); + } + + textures.clear(); + textures.resize(swapchain->get_num_frames()); + for (auto& t : textures) + { + t.init(context.get()); + t.create(256, 224, vk::Format::eR5G6B5UnormPack16, vk::SamplerAddressMode::eClampToEdge, Settings.BilinearFilter, false); + } + + vk::SamplerCreateInfo sci{}; + sci.setAddressModeU(vk::SamplerAddressMode::eClampToEdge) + .setAddressModeV(vk::SamplerAddressMode::eClampToEdge) + .setAddressModeW(vk::SamplerAddressMode::eClampToEdge) + .setMipmapMode(vk::SamplerMipmapMode::eLinear) + .setAnisotropyEnable(false) + .setMinFilter(vk::Filter::eLinear) + .setMagFilter(vk::Filter::eLinear) + .setUnnormalizedCoordinates(false) + .setMinLod(1.0f) + .setMaxLod(1.0f) + .setMipLodBias(0.0) + .setCompareEnable(false); + linear_sampler = device.createSampler(sci); + + sci.setMinFilter(vk::Filter::eNearest) + .setMagFilter(vk::Filter::eNearest); + nearest_sampler = device.createSampler(sci); return true; } void CVulkan::DeInitialize() { + if (!context) + return; + + context->wait_idle(); + textures.clear(); + descriptors.clear(); + device.destroySampler(linear_sampler); + device.destroySampler(nearest_sampler); + swapchain = nullptr; + descriptor_set_layout.reset(); + pipeline_layout.reset(); + pipeline.reset(); + textures.clear(); + descriptors.clear(); + filtered_image.clear(); + context.reset(); } void CVulkan::Render(SSurface Src) { + if (!context) + return; + + if (GUI.ReduceInputLag) + device.waitIdle(); + + SSurface Dst; + + RECT dstRect = GetFilterOutputSize(Src); + Dst.Width = dstRect.right - dstRect.left; + Dst.Height = dstRect.bottom - dstRect.top; + Dst.Pitch = Dst.Width << 1; + size_t requiredSize = Dst.Width * Dst.Height; + if (filtered_image.size() < requiredSize) + filtered_image.resize(requiredSize * 2); + Dst.Surface = (unsigned char *)filtered_image.data(); + + RenderMethod(Src, Dst, &dstRect); + + if (!Settings.AutoDisplayMessages) { + WinSetCustomDisplaySurface((void*)Dst.Surface, Dst.Pitch / 2, dstRect.right - dstRect.left, dstRect.bottom - dstRect.top, GetFilterScale(GUI.Scale)); + S9xDisplayMessages((uint16*)Dst.Surface, Dst.Pitch / 2, dstRect.right - dstRect.left, dstRect.bottom - dstRect.top, GetFilterScale(GUI.Scale)); + } + + RECT windowSize, displayRect; + GetClientRect(hWnd, &windowSize); + //Get maximum rect respecting AR setting + displayRect = CalculateDisplayRect(Dst.Width, Dst.Height, windowSize.right, windowSize.bottom); + + if (!swapchain->begin_frame()) + return; + + auto& tex = textures[swapchain->get_current_frame()]; + auto& cmd = swapchain->get_cmd(); + auto extents = swapchain->get_extents(); + auto& dstset = descriptors[swapchain->get_current_frame()].get(); + + tex.from_buffer(cmd, (uint8_t*)filtered_image.data(), Dst.Width, Dst.Height, Dst.Pitch); + + swapchain->begin_render_pass(); + + vk::DescriptorImageInfo dii{}; + dii.setImageView(tex.image_view) + .setSampler(Settings.BilinearFilter ? linear_sampler : nearest_sampler) + .setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + vk::WriteDescriptorSet wds{}; + wds.setDescriptorCount(1) + .setDstBinding(0) + .setDstArrayElement(0) + .setDstSet(dstset) + .setDescriptorType(vk::DescriptorType::eCombinedImageSampler) + .setImageInfo(dii); + device.updateDescriptorSets(wds, {}); + + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.get()); + cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout.get(), 0, dstset, {}); + cmd.setViewport(0, vk::Viewport(displayRect.left, displayRect.top, displayRect.right - displayRect.left, displayRect.bottom - displayRect.top, 0.0f, 1.0f)); + cmd.setScissor(0, vk::Rect2D({}, extents)); + cmd.draw(3, 1, 0, 0); + + swapchain->end_render_pass(); + swapchain->end_frame(); } bool CVulkan::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight) { - return false; + if (!context) + return false; + + bool vsync_changed = context->swapchain->set_vsync(GUI.Vsync); + + if (newWidth != current_width || newHeight != current_height || vsync_changed) + { + context->recreate_swapchain(newWidth, newHeight); + context->wait_idle(); + current_width = newWidth; + current_height = newHeight; + } + + context->swapchain->set_vsync(GUI.Vsync); + + return true; } bool CVulkan::ApplyDisplayChanges(void) @@ -48,8 +184,189 @@ bool CVulkan::SetFullscreen(bool fullscreen) void CVulkan::SetSnes9xColorFormat() { + GUI.ScreenDepth = 16; + GUI.BlueShift = 0; + GUI.GreenShift = 6; + GUI.RedShift = 11; + S9xBlit2xSaIFilterInit(); + S9xBlitHQ2xFilterInit(); + GUI.NeedDepthConvert = FALSE; + GUI.DepthConverted = TRUE; } void CVulkan::EnumModes(std::vector* modeVector) { + DISPLAY_DEVICE dd; + dd.cb = sizeof(dd); + DWORD dev = 0; + int iMode = 0; + dMode mode; + + while (EnumDisplayDevices(0, dev, &dd, 0)) + { + if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) && (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) + { + DEVMODE dm; + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + iMode = 0; + while (EnumDisplaySettings(dd.DeviceName, iMode, &dm)) { + if (dm.dmBitsPerPel >= 32) { + mode.width = dm.dmPelsWidth; + mode.height = dm.dmPelsHeight; + mode.rate = dm.dmDisplayFrequency; + mode.depth = dm.dmBitsPerPel; + modeVector->push_back(mode); + } + iMode++; + } + } + dev++; + } } + +static const char* vertex_shader = R"( +#version 450 + +layout(location = 0) out vec2 texcoord; + +vec2 positions[3] = vec2[](vec2(-1.0, -3.0), vec2(3.0, 1.0), vec2(-1.0, 1.0)); +vec2 texcoords[3] = vec2[](vec2(0.0, -1.0), vec2(2.0, 1.0), vec2(0.0, 1.0)); + +void main() +{ + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + texcoord = texcoords[gl_VertexIndex]; +} +)"; + +static const char* fragment_shader = R"( +#version 450 + +layout(location = 0) in vec2 texcoord; +layout(binding = 0) uniform sampler2D tsampler; + +layout(location = 0) out vec4 fragcolor; + +void main() +{ + fragcolor = texture(tsampler, texcoord); +} +)"; + +void CVulkan::create_pipeline() +{ + auto vertex_spirv = SlangShader::generate_spirv(vertex_shader, "vertex"); + auto fragment_spirv = SlangShader::generate_spirv(fragment_shader, "fragment"); + + auto vertex_module = device.createShaderModuleUnique({ {}, vertex_spirv }); + auto fragment_module = device.createShaderModuleUnique({ {}, fragment_spirv }); + + vk::PipelineShaderStageCreateInfo vertex_ci; + vertex_ci.setStage(vk::ShaderStageFlagBits::eVertex) + .setModule(vertex_module.get()) + .setPName("main"); + + vk::PipelineShaderStageCreateInfo fragment_ci; + fragment_ci.setStage(vk::ShaderStageFlagBits::eFragment) + .setModule(fragment_module.get()) + .setPName("main"); + + std::vector stages = { vertex_ci, fragment_ci }; + + vk::PipelineVertexInputStateCreateInfo vertex_input_info{}; + + vk::PipelineInputAssemblyStateCreateInfo pipeline_input_assembly_info{}; + pipeline_input_assembly_info.setTopology(vk::PrimitiveTopology::eTriangleList) + .setPrimitiveRestartEnable(false); + + std::vector viewports(1); + viewports[0] + .setX(0.0f) + .setY(0.0f) + .setWidth(256) + .setHeight(256) + .setMinDepth(0.0f) + .setMaxDepth(1.0f); + std::vector scissors(1); + scissors[0].extent.width = 256; + scissors[0].extent.height = 256; + scissors[0].offset = vk::Offset2D(0, 0); + + vk::PipelineViewportStateCreateInfo pipeline_viewport_info; + pipeline_viewport_info.setViewports(viewports) + .setScissors(scissors); + + vk::PipelineRasterizationStateCreateInfo rasterizer_info; + rasterizer_info.setCullMode(vk::CullModeFlagBits::eBack) + .setFrontFace(vk::FrontFace::eClockwise) + .setLineWidth(1.0f) + .setDepthClampEnable(false) + .setRasterizerDiscardEnable(false) + .setPolygonMode(vk::PolygonMode::eFill) + .setDepthBiasEnable(false) + .setRasterizerDiscardEnable(false); + + vk::PipelineMultisampleStateCreateInfo multisample_info; + multisample_info.setSampleShadingEnable(false) + .setRasterizationSamples(vk::SampleCountFlagBits::e1); + + vk::PipelineDepthStencilStateCreateInfo depth_stencil_info; + depth_stencil_info.setDepthTestEnable(false); + + vk::PipelineColorBlendAttachmentState blend_attachment_info; + blend_attachment_info + .setColorWriteMask(vk::ColorComponentFlagBits::eB | + vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eR | + vk::ColorComponentFlagBits::eA) + .setBlendEnable(true) + .setColorBlendOp(vk::BlendOp::eAdd) + .setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha) + .setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha) + .setAlphaBlendOp(vk::BlendOp::eAdd) + .setSrcAlphaBlendFactor(vk::BlendFactor::eOne) + .setSrcAlphaBlendFactor(vk::BlendFactor::eZero); + + vk::PipelineColorBlendStateCreateInfo blend_state_info; + blend_state_info.setLogicOpEnable(false) + .setAttachments(blend_attachment_info); + + std::vector states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; + vk::PipelineDynamicStateCreateInfo dynamic_state_info({}, states); + + vk::DescriptorSetLayoutBinding dslb{}; + dslb.setBinding(0) + .setStageFlags(vk::ShaderStageFlagBits::eFragment) + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eCombinedImageSampler); + vk::DescriptorSetLayoutCreateInfo dslci{}; + dslci.setBindings(dslb); + descriptor_set_layout = device.createDescriptorSetLayoutUnique(dslci); + + vk::PipelineLayoutCreateInfo pipeline_layout_info; + pipeline_layout_info.setSetLayoutCount(0) + .setPushConstantRangeCount(0) + .setSetLayouts(descriptor_set_layout.get()); + + pipeline_layout = device.createPipelineLayoutUnique(pipeline_layout_info); + + vk::GraphicsPipelineCreateInfo pipeline_create_info; + pipeline_create_info.setStageCount(2) + .setStages(stages) + .setPVertexInputState(&vertex_input_info) + .setPInputAssemblyState(&pipeline_input_assembly_info) + .setPViewportState(&pipeline_viewport_info) + .setPRasterizationState(&rasterizer_info) + .setPMultisampleState(&multisample_info) + .setPDepthStencilState(&depth_stencil_info) + .setPColorBlendState(&blend_state_info) + .setPDynamicState(&dynamic_state_info) + .setLayout(pipeline_layout.get()) + .setRenderPass(swapchain->get_render_pass()) + .setSubpass(0); + + auto [result, pipeline] = device.createGraphicsPipelineUnique(nullptr, pipeline_create_info); + this->pipeline = std::move(pipeline); +} + diff --git a/win32/CVulkan.h b/win32/CVulkan.h index 73ee00bf..d20b18a1 100644 --- a/win32/CVulkan.h +++ b/win32/CVulkan.h @@ -1,23 +1,31 @@ #pragma once -#include #include "IS9xDisplayOutput.h" -#include "../../../vulkan/vulkan_context.hpp" +#include "../vulkan/vulkan_context.hpp" +#include "../vulkan/vulkan_texture.hpp" class CVulkan : public IS9xDisplayOutput { private: + void create_pipeline(); + HWND hWnd; std::unique_ptr context; Vulkan::Swapchain* swapchain; vk::Device device; + vk::UniqueDescriptorSetLayout descriptor_set_layout; + vk::UniquePipelineLayout pipeline_layout; + vk::UniquePipeline pipeline; + vk::Sampler linear_sampler; + vk::Sampler nearest_sampler; + std::vector textures; + std::vector descriptors; + std::vector filtered_image; int current_width; int current_height; public: - CVulkan(); - ~CVulkan(); bool Initialize(HWND hWnd); void DeInitialize(); void Render(SSurface Src); diff --git a/win32/win32_display.cpp b/win32/win32_display.cpp index c4448ef1..7ed53079 100644 --- a/win32/win32_display.cpp +++ b/win32/win32_display.cpp @@ -19,6 +19,7 @@ #include "CDirectDraw.h" #endif #include "COpenGL.h" +#include "CVulkan.h" #include "IS9xDisplayOutput.h" #include "../filter/hq2x.h" @@ -31,6 +32,7 @@ CDirect3D Direct3D; CDirectDraw DirectDraw; #endif COpenGL OpenGL; +CVulkan VulkanDriver; SSurface Src = {0}; extern BYTE *ScreenBufferBlend; @@ -118,6 +120,9 @@ bool WinDisplayReset(void) case OPENGL: S9xDisplayOutput = &OpenGL; break; + case VULKAN: + S9xDisplayOutput = &VulkanDriver; + break; } if(S9xDisplayOutput->Initialize(GUI.hWnd)) { S9xGraphicsDeinit();