diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 7fb9b5fb7..e6ed254ef 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -261,6 +261,9 @@ create_target_directory_groups(citra-qt) target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia) target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) +endif() target_compile_definitions(citra-qt PRIVATE # Use QStringBuilder for string concatenation to reduce diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 8b4c145bf..d98f32dce 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -27,6 +27,10 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" +#if !defined(WIN32) +#include +#endif + EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {} EmuThread::~EmuThread() = default; @@ -191,6 +195,40 @@ void OpenGLWindow::exposeEvent(QExposeEvent* event) { QWindow::exposeEvent(event); } +static Frontend::WindowSystemType GetWindowSystemType() { + // Determine WSI type based on Qt platform. + QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("windows")) + return Frontend::WindowSystemType::Windows; + else if (platform_name == QStringLiteral("xcb")) + return Frontend::WindowSystemType::X11; + else if (platform_name == QStringLiteral("wayland")) + return Frontend::WindowSystemType::Wayland; + + LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + return Frontend::WindowSystemType::Windows; +} + +static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { + Frontend::EmuWindow::WindowSystemInfo wsi; + wsi.type = GetWindowSystemType(); + + // Our Win32 Qt external doesn't have the private API. +#if defined(WIN32) || defined(__APPLE__) + wsi.render_surface = window ? reinterpret_cast(window->winId()) : nullptr; +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + wsi.display_connection = pni->nativeResourceForWindow("display", window); + if (wsi.type == Frontend::WindowSystemType::Wayland) + wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr; + else + wsi.render_surface = window ? reinterpret_cast(window->winId()) : nullptr; +#endif + wsi.render_surface_scale = window ? static_cast(window->devicePixelRatio()) : 1.0f; + + return wsi; +} + GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) : QWidget(parent_), emu_thread(emu_thread) { @@ -396,6 +434,9 @@ void GRenderWindow::InitRenderTarget() { child_widget = createWindowContainer(child_window, this); child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); + // Update the Window System information with the new render target + window_info = GetWindowSystemInfo(child_widget->windowHandle()); + layout()->addWidget(child_widget); core_context = CreateSharedContext(); diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 326563655..4a09440fc 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -12,6 +12,15 @@ namespace Frontend { +/// Information for the Graphics Backends signifying what type of screen pointer is in +/// WindowInformation +enum class WindowSystemType { + Headless, + Windows, + X11, + Wayland, +}; + struct Frame; /** * For smooth Vsync rendering, we want to always present the latest frame that the core generates, @@ -95,6 +104,23 @@ public: std::pair min_client_area_size; }; + /// Data describing host window system information + struct WindowSystemInfo { + // Window system type. Determines which GL context or Vulkan WSI is used. + WindowSystemType type = WindowSystemType::Headless; + + // Connection to a display server. This is used on X11 and Wayland platforms. + void* display_connection = nullptr; + + // Render surface. This is a pointer to the native window handle, which depends + // on the platform. e.g. HWND for Windows, Window for X11. If the surface is + // set to nullptr, the video backend will run in headless mode. + void* render_surface = nullptr; + + // Scale of the render surface. For hidpi systems, this will be >1. + float render_surface_scale = 1.0f; + }; + /// Polls window events virtual void PollEvents() = 0; @@ -148,6 +174,13 @@ public: config = val; } + /** + * Returns system information about the drawing area. + */ + const WindowSystemInfo& GetWindowInfo() const { + return window_info; + } + /** * Gets the framebuffer layout (width, height, and screen regions) * @note This method is thread-safe @@ -194,6 +227,8 @@ protected: framebuffer_layout = layout; } + WindowSystemInfo window_info; + private: /** * Handler called when the minimal client area was requested to be changed via SetConfig. diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 532baab08..70d117ea9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -75,8 +75,8 @@ add_library(video_core STATIC renderer_vulkan/vk_buffer.h renderer_vulkan/vk_instance.cpp renderer_vulkan/vk_instance.h - renderer_vulkan/vk_resource_cache.cpp - renderer_vulkan/vk_resource_cache.h + renderer_vulkan/vk_pipeline_builder.cpp + renderer_vulkan/vk_pipeline_builder.h renderer_vulkan/vk_rasterizer_cache.cpp renderer_vulkan/vk_rasterizer_cache.h renderer_vulkan/vk_rasterizer.cpp diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index b2909b11e..e32766207 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -31,194 +31,103 @@ #include "video_core/rasterizer_interface.h" #include "video_core/renderer_vulkan/vk_state.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_task_scheduler.h" #include "video_core/video_core.h" +// Include these late to avoid polluting previous headers +#ifdef _WIN32 +#include +// ensure include order +#include +#endif + +#if !defined(_WIN32) && !defined(__APPLE__) +#include +#include +#include +#endif + namespace Vulkan { -class OGLTextureMailboxException : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; +vk::SurfaceKHR CreateSurface(const VkInstance& instance, + const Frontend::EmuWindow& emu_window) { + const auto& window_info = emu_window.GetWindowInfo(); + VkSurfaceKHR unsafe_surface = nullptr; -class OGLTextureMailbox : public Frontend::TextureMailbox { -public: - std::mutex swap_chain_lock; - std::condition_variable free_cv; - std::condition_variable present_cv; - std::array swap_chain{}; - std::queue free_queue{}; - std::deque present_queue{}; - Frontend::Frame* previous_frame = nullptr; - - OGLTextureMailbox() { - for (auto& frame : swap_chain) { - free_queue.push(&frame); +#ifdef _WIN32 + if (window_info.type == Core::Frontend::WindowSystemType::Windows) { + const HWND hWnd = static_cast(window_info.render_surface); + const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + nullptr, 0, nullptr, hWnd}; + if (vkCreateWin32SurfaceKHR(instance, &win32_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface"); + UNREACHABLE(); + } + } +#endif +#if !defined(_WIN32) && !defined(__APPLE__) + if (window_info.type == Frontend::WindowSystemType::X11) { + const VkXlibSurfaceCreateInfoKHR xlib_ci{ + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, + static_cast(window_info.display_connection), + reinterpret_cast(window_info.render_surface)}; + if (vkCreateXlibSurfaceKHR(instance, &xlib_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); + UNREACHABLE(); } } - ~OGLTextureMailbox() override { - // lock the mutex and clear out the present and free_queues and notify any people who are - // blocked to prevent deadlock on shutdown - std::scoped_lock lock(swap_chain_lock); - std::queue().swap(free_queue); - present_queue.clear(); - present_cv.notify_all(); - free_cv.notify_all(); - } - - void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override { - frame->present.Release(); - frame->present.Create(); - GLint previous_draw_fbo{}; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); - glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - frame->color.handle); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); + if (window_info.type == Frontend::WindowSystemType::Wayland) { + const VkWaylandSurfaceCreateInfoKHR wayland_ci{ + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, + static_cast(window_info.display_connection), + static_cast(window_info.render_surface)}; + if (vkCreateWaylandSurfaceKHR(instance, &wayland_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); + UNREACHABLE(); } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); - frame->color_reloaded = false; + } +#endif + if (!unsafe_surface) { + LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); + UNREACHABLE(); } - void ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) override { - OpenGLState prev_state = OpenGLState::GetCurState(); - OpenGLState state = OpenGLState::GetCurState(); + return vk::SurfaceKHR(unsafe_surface); +} - // Recreate the color texture attachment - frame->color.Release(); - frame->color.Create(); - state.renderbuffer = frame->color.handle; - state.Apply(); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); - - // Recreate the FBO for the render target - frame->render.Release(); - frame->render.Create(); - state.draw.read_framebuffer = frame->render.handle; - state.draw.draw_framebuffer = frame->render.handle; - state.Apply(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - frame->color.handle); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); - } - prev_state.Apply(); - frame->width = width; - frame->height = height; - frame->color_reloaded = true; +std::vector RequiredExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils) { + std::vector extensions; + extensions.reserve(6); + switch (window_type) { + case Frontend::WindowSystemType::Headless: + break; +#ifdef _WIN32 + case Frontend::WindowSystemType::Windows: + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + break; +#endif +#if !defined(_WIN32) && !defined(__APPLE__) + case Frontend::WindowSystemType::X11: + extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + break; + case Frontend::WindowSystemType::Wayland: + extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + break; +#endif + default: + LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); + break; } - - Frontend::Frame* GetRenderFrame() override { - std::unique_lock lock(swap_chain_lock); - - // If theres no free frames, we will reuse the oldest render frame - if (free_queue.empty()) { - auto frame = present_queue.back(); - present_queue.pop_back(); - return frame; - } - - Frontend::Frame* frame = free_queue.front(); - free_queue.pop(); - return frame; + if (window_type != Frontend::WindowSystemType::Headless) { + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); } - - void ReleaseRenderFrame(Frontend::Frame* frame) override { - std::unique_lock lock(swap_chain_lock); - present_queue.push_front(frame); - present_cv.notify_one(); + if (enable_debug_utils) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - - // This is virtual as it is to be overriden in OGLVideoDumpingMailbox below. - virtual void LoadPresentFrame() { - // free the previous frame and add it back to the free queue - if (previous_frame) { - free_queue.push(previous_frame); - free_cv.notify_one(); - } - - // the newest entries are pushed to the front of the queue - Frontend::Frame* frame = present_queue.front(); - present_queue.pop_front(); - // remove all old entries from the present queue and move them back to the free_queue - for (auto f : present_queue) { - free_queue.push(f); - } - present_queue.clear(); - previous_frame = frame; - } - - Frontend::Frame* TryGetPresentFrame(int timeout_ms) override { - std::unique_lock lock(swap_chain_lock); - // wait for new entries in the present_queue - present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), - [&] { return !present_queue.empty(); }); - if (present_queue.empty()) { - // timed out waiting for a frame to draw so return the previous frame - return previous_frame; - } - - LoadPresentFrame(); - return previous_frame; - } -}; - -/// This mailbox is different in that it will never discard rendered frames -class OGLVideoDumpingMailbox : public OGLTextureMailbox { -public: - bool quit = false; - - Frontend::Frame* GetRenderFrame() override { - std::unique_lock lock(swap_chain_lock); - - // If theres no free frames, we will wait until one shows up - if (free_queue.empty()) { - free_cv.wait(lock, [&] { return (!free_queue.empty() || quit); }); - if (quit) { - throw OGLTextureMailboxException("VideoDumpingMailbox quitting"); - } - - if (free_queue.empty()) { - LOG_CRITICAL(Render_OpenGL, "Could not get free frame"); - return nullptr; - } - } - - Frontend::Frame* frame = free_queue.front(); - free_queue.pop(); - return frame; - } - - void LoadPresentFrame() override { - // free the previous frame and add it back to the free queue - if (previous_frame) { - free_queue.push(previous_frame); - free_cv.notify_one(); - } - - Frontend::Frame* frame = present_queue.back(); - present_queue.pop_back(); - previous_frame = frame; - - // Do not remove entries from the present_queue, as video dumping would require - // that we preserve all frames - } - - Frontend::Frame* TryGetPresentFrame(int timeout_ms) override { - std::unique_lock lock(swap_chain_lock); - // wait for new entries in the present_queue - present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), - [&] { return !present_queue.empty(); }); - if (present_queue.empty()) { - // timed out waiting for a frame - return nullptr; - } - - LoadPresentFrame(); - return previous_frame; - } -}; + extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + return extensions; +} static const char vertex_shader[] = R"( in vec2 vert_position; @@ -353,14 +262,12 @@ static std::array MakeOrthographicMatrix(const float width, cons } RendererVulkan::RendererVulkan(Frontend::EmuWindow& window) - : RendererBase{window}, frame_dumper(Core::System::GetInstance().VideoDumper(), window) { + : RendererBase{window} { - window.mailbox = std::make_unique(); - frame_dumper.mailbox = std::make_unique(); + window.mailbox = nullptr; + swapchain = std::make_unique(CreateSurface(nullptr, window)); } -RendererVulkan::~RendererVulkan() = default; - MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); @@ -372,19 +279,9 @@ void RendererVulkan::SwapBuffers() { PrepareRendertarget(); - RenderScreenshot(); - const auto& layout = render_window.GetFramebufferLayout(); RenderToMailbox(layout, render_window.mailbox, false); - if (frame_dumper.IsDumping()) { - try { - RenderToMailbox(frame_dumper.GetLayout(), frame_dumper.mailbox, true); - } catch (const OGLTextureMailboxException& exception) { - LOG_DEBUG(Render_OpenGL, "Frame dumper exception caught: {}", exception.what()); - } - } - m_current_frame++; Core::System::GetInstance().perf_stats->EndSystemFrame(); @@ -403,42 +300,8 @@ void RendererVulkan::SwapBuffers() { } } -void RendererVulkan::RenderScreenshot() { - if (VideoCore::g_renderer_screenshot_requested) { - // Draw this frame to the screenshot framebuffer - screenshot_framebuffer.Create(); - GLuint old_read_fb = state.draw.read_framebuffer; - GLuint old_draw_fb = state.draw.draw_framebuffer; - state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle; - state.Apply(); - - Layout::FramebufferLayout layout{VideoCore::g_screenshot_framebuffer_layout}; - - GLuint renderbuffer; - glGenRenderbuffers(1, &renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - renderbuffer); - - DrawScreens(layout, false); - - glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - VideoCore::g_screenshot_bits); - - screenshot_framebuffer.Release(); - state.draw.read_framebuffer = old_read_fb; - state.draw.draw_framebuffer = old_draw_fb; - state.Apply(); - glDeleteRenderbuffers(1, &renderbuffer); - - VideoCore::g_screenshot_complete_callback(); - VideoCore::g_renderer_screenshot_requested = false; - } -} - void RendererVulkan::PrepareRendertarget() { - for (int i : {0, 1, 2}) { + for (int i = 0; i < 3; i++) { int fb_id = i == 2 ? 1 : 0; const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id]; @@ -450,21 +313,18 @@ void RendererVulkan::PrepareRendertarget() { LCD::Read(color_fill.raw, lcd_color_addr); if (color_fill.is_enabled) { - LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, - screen_infos[i].texture); - - // Resize the texture in case the framebuffer size has changed - screen_infos[i].texture.width = 1; - screen_infos[i].texture.height = 1; + LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, screen_infos[i]); } else { - if (screen_infos[i].texture.width != (GLsizei)framebuffer.width || - screen_infos[i].texture.height != (GLsizei)framebuffer.height || - screen_infos[i].texture.format != framebuffer.color_format) { + auto rect = screen_infos[i].texture->GetExtent(); + auto format = screen_infos[i].format; + if (rect.width != framebuffer.width || rect.height != framebuffer.height || + format != framebuffer.color_format) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a // performance problem. - ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer); + ConfigureFramebufferTexture(screen_infos[i], framebuffer); } + LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1); // Resize the texture in case the framebuffer size has changed @@ -483,27 +343,6 @@ void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout, MICROPROFILE_SCOPE(OpenGL_WaitPresent); frame = mailbox->GetRenderFrame(); - - // Clean up sync objects before drawing - - // INTEL driver workaround. We can't delete the previous render sync object until we are - // sure that the presentation is done - if (frame->present_fence) { - glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); - } - - // delete the draw fence if the frame wasn't presented - if (frame->render_fence) { - glDeleteSync(frame->render_fence); - frame->render_fence = nullptr; - } - - // wait for the presentation to be done - if (frame->present_fence) { - glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(frame->present_fence); - frame->present_fence = nullptr; - } } { @@ -519,8 +358,6 @@ void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout, state.Apply(); DrawScreens(layout, flipped); // Create a fence for the frontend to wait on and swap this frame to OffTex - frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); mailbox->ReleaseRenderFrame(frame); } } @@ -539,7 +376,7 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram ? (!right_eye ? framebuffer.address_left1 : framebuffer.address_right1) : (!right_eye ? framebuffer.address_left2 : framebuffer.address_right2); - LOG_TRACE(Render_OpenGL, "0x{:08x} bytes from 0x{:08x}({}x{}), fmt {:x}", + LOG_TRACE(Render_Vulkan, "0x{:08x} bytes from 0x{:08x}({}x{}), fmt {:x}", framebuffer.stride * framebuffer.height, framebuffer_addr, framebuffer.width.Value(), framebuffer.height.Value(), framebuffer.format); @@ -553,35 +390,18 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram // only allows rows to have a memory alignement of 4. ASSERT(pixel_stride % 4 == 0); - if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, - static_cast(pixel_stride), screen_info)) { + if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast(pixel_stride), screen_info)) { // Reset the screen info's display texture to its own permanent texture - screen_info.display_texture = screen_info.texture.resource.handle; + screen_info.display_texture = screen_info.texture; screen_info.display_texcoords = Common::Rectangle(0.f, 0.f, 1.f, 1.f); Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); - const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr); + vk::Rect2D region{{0, 0}, {framebuffer.width, framebuffer.height}}; + std::span framebuffer_data(VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr), + screen_info.texture->GetSize()); - state.texture_units[0].texture_2d = screen_info.texture.resource.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); - - // Update existing texture - // TODO: Test what happens on hardware when you change the framebuffer dimensions so that - // they differ from the LCD resolution. - // TODO: Applications could theoretically crash Citra here by specifying too large - // framebuffer sizes. We should make sure that this cannot happen. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, - screen_info.texture.gl_format, screen_info.texture.gl_type, - framebuffer_data); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - state.texture_units[0].texture_2d = 0; - state.Apply(); + screen_info.texture->Upload(0, 1, pixel_stride, region, framebuffer_data); } } @@ -590,7 +410,7 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram * be 1x1 but will stretch across whatever it's rendered on. */ void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, - const TextureInfo& texture) { + const ScreenInfo& screen) { state.texture_units[0].texture_2d = texture.resource.handle; state.Apply(); @@ -671,8 +491,8 @@ void RendererVulkan::ReloadSampler() { void RendererVulkan::ReloadShader() { // Link shaders and get variable locations - std::string shader_data; - if (GLES) { + std::string shader_data = fragment_shader; + /*if (GLES) { shader_data += fragment_shader_precision_OES; } if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) { @@ -715,7 +535,7 @@ void RendererVulkan::ReloadShader() { shader_data += shader_text; } } - } + }*/ shader.Create(vertex_shader, shader_data.c_str()); state.draw.shader_program = shader.handle; state.Apply(); @@ -948,8 +768,7 @@ void RendererVulkan::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) { if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) { // Update background color before drawing - glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, - 0.0f); + glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); } if (VideoCore::g_renderer_sampler_update_requested.exchange(false)) { @@ -1112,10 +931,13 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f } void RendererVulkan::TryPresent(int timeout_ms) { + + g_vk_task_scheduler->Submit(true); + const auto& layout = render_window.GetFramebufferLayout(); auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms); if (!frame) { - LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); + LOG_DEBUG(Render_Vulkan, "TryGetPresentFrame returned no frame to present"); return; } @@ -1125,9 +947,10 @@ void RendererVulkan::TryPresent(int timeout_ms) { // Recreate the presentation FBO if the color attachment was changed if (frame->color_reloaded) { - LOG_DEBUG(Render_OpenGL, "Reloading present frame"); + LOG_DEBUG(Render_Vulkan, "Reloading present frame"); render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height); } + glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); // INTEL workaround. // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete @@ -1154,25 +977,6 @@ void RendererVulkan::TryPresent(int timeout_ms) { /// Updates the framerate void RendererVulkan::UpdateFramerate() {} -void RendererVulkan::PrepareVideoDumping() { - auto* mailbox = static_cast(frame_dumper.mailbox.get()); - { - std::unique_lock lock(mailbox->swap_chain_lock); - mailbox->quit = false; - } - frame_dumper.StartDumping(); -} - -void RendererVulkan::CleanupVideoDumping() { - frame_dumper.StopDumping(); - auto* mailbox = static_cast(frame_dumper.mailbox.get()); - { - std::unique_lock lock(mailbox->swap_chain_lock); - mailbox->quit = true; - } - mailbox->free_cv.notify_one(); -} - static const char* GetSource(GLenum source) { #define RET(s) \ case GL_DEBUG_SOURCE_##s: \ @@ -1229,40 +1033,28 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum /// Initialize the renderer VideoCore::ResultStatus RendererVulkan::Init() { -#ifndef ANDROID - if (!gladLoadGL()) { - return VideoCore::ResultStatus::ErrorBelowGL33; - } + // Create vulkan instance + vk::ApplicationInfo app_info("PS2 Emulator", 1, nullptr, 0, VK_API_VERSION_1_3); - // Qualcomm has some spammy info messages that are marked as errors but not important - // https://developer.qualcomm.com/comment/11845 - if (GLAD_GL_KHR_debug) { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(DebugHandler, nullptr); - } -#endif + // Get required extensions + auto extensions = RequiredExtensions(render_window.GetWindowInfo().type, true); - const char* gl_version{reinterpret_cast(glGetString(GL_VERSION))}; - const char* gpu_vendor{reinterpret_cast(glGetString(GL_VENDOR))}; - const char* gpu_model{reinterpret_cast(glGetString(GL_RENDERER))}; + const char* layers = "VK_LAYER_KHRONOS_validation"; + vk::InstanceCreateInfo instance_info{{}, &app_info, layers, extensions}; - LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); - LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); - LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); + auto instance = vk::createInstance(instance_info); + auto surface = swapchain->GetSurface(); + auto physical_device = instance.enumeratePhysicalDevices()[0]; + + // Create global instance + g_vk_instace = std::make_unique(); + g_vk_instace->Create(instance, physical_device, surface, true); auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; - telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor)); - telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model)); - telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); - - if (!strcmp(gpu_vendor, "GDI Generic")) { - return VideoCore::ResultStatus::ErrorGenericDrivers; - } - - if (!(GLAD_GL_VERSION_3_3 || GLAD_GL_ES_VERSION_3_1)) { - return VideoCore::ResultStatus::ErrorBelowGL33; - } + telemetry_session.AddField(user_system, "GPU_Vendor", "NVIDIA"); + telemetry_session.AddField(user_system, "GPU_Model", "GTX 1650"); + telemetry_session.AddField(user_system, "GPU_Vulkan_Version", "Vulkan 1.3"); InitOpenGLObjects(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 04123b736..663ff7324 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -10,6 +10,7 @@ #include "core/hw/gpu.h" #include "video_core/renderer_base.h" #include "video_core/renderer_vulkan/vk_resource_cache.h" +#include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_state.h" namespace Layout { @@ -38,8 +39,8 @@ struct ScreenInfo { class RendererVulkan : public RendererBase { public: - explicit RendererVulkan(Frontend::EmuWindow& window); - ~RendererVulkan() override; + RendererVulkan(Frontend::EmuWindow& window); + ~RendererVulkan() override = default; /// Initialize the renderer VideoCore::ResultStatus Init() override; @@ -59,7 +60,6 @@ private: void ReloadSampler(); void ReloadShader(); void PrepareRendertarget(); - void RenderScreenshot(); void RenderToMailbox(const Layout::FramebufferLayout& layout, std::unique_ptr& mailbox, bool flipped); void ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer); @@ -83,11 +83,12 @@ private: // OpenGL object IDs VKBuffer vertex_buffer; - OGLProgram shader; - OGLSampler filter_sampler; + //OGLProgram shader; + //OGLSampler filter_sampler; /// Display information for top and bottom screens respectively std::array screen_infos; + std::unique_ptr swapchain; }; } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index a02aa6091..97339e6e3 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -72,7 +72,7 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) { static constexpr float queue_priorities[] = { 1.0f }; vk::DeviceCreateInfo device_info; - device_info.setPEnabledExtensionNames(device_extensions); + device_info.setPEnabledExtensionNames(extensions); // Create queue create info structs if (graphics_queue_family_index != present_queue_family_index) { @@ -92,8 +92,7 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) { } // Set device features - device_info.setPEnabledFeatures(&device_features); - device_info.setPNext(&new_features); + device_info.setPEnabledFeatures(&vk_features); // Enable debug layer on debug builds if (validation_enabled) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp b/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp new file mode 100644 index 000000000..48e7baff1 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp @@ -0,0 +1,144 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/renderer_vulkan/vk_pipeline_builder.h" +#include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_shader_state.h" +#include +#include +#include + +namespace Vulkan { + +PipelineBuilder::PipelineBuilder() { + vertex_input_state = vk::PipelineVertexInputStateCreateInfo{ + {}, HardwareVertex::binding_desc, HardwareVertex::attribute_desc + }; + + /* Include all required pointers to the pipeline info structure */ + vk::GraphicsPipelineCreateInfo pipeline_info{ + {}, 0, shader_stages.data(), &vertex_input_state, &input_assembly, nullptr, + &viewport_state, &rasterization_state, &multisample_info, &depth_state, + &blend_state, &dynamic_info, nullptr, nullptr }; +} + +vk::Pipeline PipelineBuilder::Build() { + auto& device = g_vk_instace->GetDevice(); + auto result = device.createGraphicsPipeline({}, pipeline_info); + + if (result.result != vk::Result::eSuccess) { + LOG_CRITICAL(Render_Vulkan, "Failed to build vulkan pipeline!"); + UNREACHABLE(); + } + + return result.value; +} + +void PipelineBuilder::SetPipelineLayout(vk::PipelineLayout layout) { + pipeline_info.layout = layout; +} + +void PipelineBuilder::SetShaderStage(vk::ShaderStageFlagBits stage, vk::ShaderModule module) { + auto result = std::ranges::find_if(shader_stages.begin(), shader_stages.end(), [stage](const auto& info) { + return info.stage == stage; + }); + + /* If the stage already exists, just replace the module */ + if (result != shader_stages.end()) { + result->module = module; + } + else { + shader_stages.emplace_back(vk::PipelineShaderStageCreateFlags(), stage, module, "main"); + pipeline_info.stageCount++; + } +} + +void PipelineBuilder::SetPrimitiveTopology(vk::PrimitiveTopology topology, bool enable_primitive_restart) { + input_assembly.topology = topology; + input_assembly.primitiveRestartEnable = enable_primitive_restart; + pipeline_info.pInputAssemblyState = &input_assembly; +} + +void PipelineBuilder::SetRasterizationState(vk::PolygonMode polygon_mode, vk::CullModeFlags cull_mode, + vk::FrontFace front_face) { + rasterization_state.polygonMode = polygon_mode; + rasterization_state.cullMode = cull_mode; + rasterization_state.frontFace = front_face; +} + +void PipelineBuilder::SetLineWidth(float width) { + rasterization_state.lineWidth = width; +} + +void PipelineBuilder::SetMultisamples(u32 multisamples, bool per_sample_shading) { + multisample_info.rasterizationSamples = static_cast(multisamples); + multisample_info.sampleShadingEnable = per_sample_shading; + multisample_info.minSampleShading = (multisamples > 1) ? 1.0f : 0.0f; +} + +void PipelineBuilder::SetDepthState(bool depth_test, bool depth_write, vk::CompareOp compare_op) { + depth_state.depthTestEnable = depth_test; + depth_state.depthWriteEnable = depth_write; + depth_state.depthCompareOp = compare_op; +} + +void PipelineBuilder::SetStencilState(bool stencil_test, vk::StencilOpState front, vk::StencilOpState back) { + depth_state.stencilTestEnable = stencil_test; + depth_state.front = front; + depth_state.back = back; +} + +void PipelineBuilder::SetBlendConstants(float r, float g, float b, float a) { + blend_state.blendConstants = std::array{r, g, b, a}; +} + +void PipelineBuilder::SetBlendAttachment(bool blend_enable, vk::BlendFactor src_factor, vk::BlendFactor dst_factor, + vk::BlendOp op, vk::BlendFactor alpha_src_factor, + vk::BlendFactor alpha_dst_factor, vk::BlendOp alpha_op, + vk::ColorComponentFlags write_mask) { + blend_attachment.blendEnable = blend_enable; + blend_attachment.srcColorBlendFactor = src_factor; + blend_attachment.dstColorBlendFactor = dst_factor; + blend_attachment.colorBlendOp = op; + blend_attachment.srcAlphaBlendFactor = alpha_src_factor; + blend_attachment.dstAlphaBlendFactor = alpha_dst_factor; + blend_attachment.alphaBlendOp = alpha_op; + blend_attachment.colorWriteMask = write_mask; + + blend_state.attachmentCount = 1; + blend_state.pAttachments = &blend_attachment; +} + +void PipelineBuilder::SetNoBlendingState() { + SetBlendAttachment(false, vk::BlendFactor::eOne, vk::BlendFactor::eZero, vk::BlendOp::eAdd, vk::BlendFactor::eOne, + vk::BlendFactor::eZero, vk::BlendOp::eAdd, vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); +} + +void PipelineBuilder::AddDynamicState(vk::DynamicState state) { + if (dynamic_info.dynamicStateCount < MAX_DYNAMIC_STATES) { + dynamic_states[dynamic_info.dynamicStateCount] = state; + + dynamic_info.dynamicStateCount++; + dynamic_info.pDynamicStates = dynamic_states.data(); + return; + } + + LOG_ERROR(Render_Vulkan, "Cannot include more dynamic states!"); + UNREACHABLE(); +} + +void PipelineBuilder::SetViewport(float x, float y, float width, float height, float min_depth, float max_depth) { + viewport = vk::Viewport{ x, y, width, height, min_depth, max_depth }; + viewport_state.pViewports = &viewport; + viewport_state.viewportCount = 1; +} + +void PipelineBuilder::SetScissorRect(s32 x, s32 y, u32 width, u32 height) { + scissor = vk::Rect2D{{x, y}, {width, height}}; + viewport_state.pScissors = &scissor; + viewport_state.scissorCount = 1u; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_builder.h b/src/video_core/renderer_vulkan/vk_pipeline_builder.h new file mode 100644 index 000000000..5fb800950 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_pipeline_builder.h @@ -0,0 +1,76 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "video_core/renderer_vulkan/vk_texture.h" + +namespace Vulkan { + +constexpr u32 MAX_DYNAMIC_STATES = 14; +constexpr u32 MAX_SHADER_STAGES = 3; + +class PipelineBuilder { +public: + PipelineBuilder(); + ~PipelineBuilder() = default; + + vk::Pipeline Build(); + + void SetPipelineLayout(vk::PipelineLayout layout); + void SetShaderStage(vk::ShaderStageFlagBits stage, vk::ShaderModule module); + + void SetPrimitiveTopology(vk::PrimitiveTopology topology, bool enable_primitive_restart = false); + void SetLineWidth(float width); + void SetMultisamples(u32 multisamples, bool per_sample_shading); + void SetRasterizationState(vk::PolygonMode polygon_mode, vk::CullModeFlags cull_mode, + vk::FrontFace front_face); + + void SetDepthState(bool depth_test, bool depth_write, vk::CompareOp compare_op); + void SetStencilState(bool stencil_test, vk::StencilOpState front, vk::StencilOpState back); + void SetNoDepthTestState(); + void SetNoStencilState(); + + void SetBlendConstants(float r, float g, float b, float a); + void SetNoBlendingState(); + void SetBlendAttachment(bool blend_enable, vk::BlendFactor src_factor, vk::BlendFactor dst_factor, + vk::BlendOp op, vk::BlendFactor alpha_src_factor, vk::BlendFactor alpha_dst_factor, + vk::BlendOp alpha_op,vk::ColorComponentFlags write_mask); + + void SetViewport(float x, float y, float width, float height, float min_depth, float max_depth); + void SetScissorRect(s32 x, s32 y, u32 width, u32 height); + void AddDynamicState(vk::DynamicState state); + void SetMultisamples(vk::SampleCountFlagBits samples); + +private: + vk::GraphicsPipelineCreateInfo pipeline_info; + std::vector shader_stages; + + vk::PipelineVertexInputStateCreateInfo vertex_input_state; + vk::PipelineInputAssemblyStateCreateInfo input_assembly; + vk::PipelineRasterizationStateCreateInfo rasterization_state; + vk::PipelineDepthStencilStateCreateInfo depth_state; + + // Blending + vk::PipelineColorBlendStateCreateInfo blend_state; + vk::PipelineColorBlendAttachmentState blend_attachment; + vk::PipelineDynamicStateCreateInfo dynamic_info; + std::array dynamic_states; + + vk::PipelineViewportStateCreateInfo viewport_state; + vk::Viewport viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; + vk::Rect2D scissor; + + // Multisampling + vk::PipelineMultisampleStateCreateInfo multisample_info; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 30f51a582..b0b82c96f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -111,65 +111,6 @@ static_assert(sizeof(VSUniformData) < 16384, struct ScreenInfo; -struct VertexBase { - VertexBase() = default; - VertexBase(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { - position[0] = v.pos.x.ToFloat32(); - position[1] = v.pos.y.ToFloat32(); - position[2] = v.pos.z.ToFloat32(); - position[3] = v.pos.w.ToFloat32(); - color[0] = v.color.x.ToFloat32(); - color[1] = v.color.y.ToFloat32(); - color[2] = v.color.z.ToFloat32(); - color[3] = v.color.w.ToFloat32(); - tex_coord0[0] = v.tc0.x.ToFloat32(); - tex_coord0[1] = v.tc0.y.ToFloat32(); - tex_coord1[0] = v.tc1.x.ToFloat32(); - tex_coord1[1] = v.tc1.y.ToFloat32(); - tex_coord2[0] = v.tc2.x.ToFloat32(); - tex_coord2[1] = v.tc2.y.ToFloat32(); - tex_coord0_w = v.tc0_w.ToFloat32(); - normquat[0] = v.quat.x.ToFloat32(); - normquat[1] = v.quat.y.ToFloat32(); - normquat[2] = v.quat.z.ToFloat32(); - normquat[3] = v.quat.w.ToFloat32(); - view[0] = v.view.x.ToFloat32(); - view[1] = v.view.y.ToFloat32(); - view[2] = v.view.z.ToFloat32(); - - if (flip_quaternion) { - normquat = -normquat; - } - } - - glm::vec4 position; - glm::vec4 color; - glm::vec2 tex_coord0; - glm::vec2 tex_coord1; - glm::vec2 tex_coord2; - float tex_coord0_w; - glm::vec4 normquat; - glm::vec3 view; -}; - -/// Structure that the hardware rendered vertices are composed of -struct HardwareVertex : public VertexBase { - HardwareVertex() = default; - HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) : VertexBase(v, flip_quaternion) {}; - static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(VertexBase)); - static constexpr std::array attribute_desc = - { - vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, position)), - vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, color)), - vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord0)), - vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord1)), - vk::VertexInputAttributeDescription(4, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord2)), - vk::VertexInputAttributeDescription(5, 0, vk::Format::eR32Sfloat, offsetof(VertexBase, tex_coord0_w)), - vk::VertexInputAttributeDescription(6, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, normquat)), - vk::VertexInputAttributeDescription(7, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexBase, view)), - }; -}; - class RasterizerVulkan : public VideoCore::RasterizerInterface { public: explicit RasterizerVulkan(Frontend::EmuWindow& emu_window); diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.cpp b/src/video_core/renderer_vulkan/vk_resource_cache.cpp deleted file mode 100644 index 068f9b94e..000000000 --- a/src/video_core/renderer_vulkan/vk_resource_cache.cpp +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2022 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "video_core/renderer_vulkan/vk_resource_cache.h" -#include "video_core/renderer_vulkan/vk_instance.h" -#include -#include -#include - -namespace Vulkan { - -VKResourceCache::~VKResourceCache() { - for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) { - g_vk_instace->GetDevice().destroyDescriptorSetLayout(descriptor_layouts[i]); - } -} - -bool VKResourceCache::Initialize() { - // Define the descriptor sets we will be using - std::array ubo_set = {{ - { 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eFragment }, // shader_data - { 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex } // pica_uniforms - }}; - - std::array texture_set = {{ - { 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex0 - { 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex1 - { 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex2 - { 3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex_cube - }}; - - std::array lut_set = {{ - { 0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_lf - { 1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_rg - { 2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment } // texture_buffer_lut_rgba - }}; - - // Create and store descriptor set layouts - std::array create_infos = {{ - { vk::DescriptorSetLayoutCreateFlags(), ubo_set }, - { vk::DescriptorSetLayoutCreateFlags(), texture_set }, - { vk::DescriptorSetLayoutCreateFlags(), lut_set } - }}; - - for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) { - descriptor_layouts[i] = g_vk_instace->GetDevice().createDescriptorSetLayout(create_infos[i]); - } - - // Create the standard descriptor set layout - vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts); - pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info); - - return true; -} - -vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Format depth_format, - vk::SampleCountFlagBits multisamples, - vk::AttachmentLoadOp load_op) { - // Search the cache if we can reuse an already created renderpass - RenderPassCacheKey key = { - .color = color_format, - .depth = depth_format, - .samples = multisamples - }; - - auto it = renderpass_cache.find(key); - if (it != renderpass_cache.end()) { - return it->second.get(); - } - - // Otherwise create a new one with the parameters provided - vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics); - std::array attachments; - std::array references; - u32 index = 0; - - if (color_format != vk::Format::eUndefined) { - references[index] = vk::AttachmentReference{index, vk::ImageLayout::eColorAttachmentOptimal}; - attachments[index] = - { - {}, - color_format, - multisamples, - load_op, - vk::AttachmentStoreOp::eStore, - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - vk::ImageLayout::eColorAttachmentOptimal, - vk::ImageLayout::eColorAttachmentOptimal - }; - - subpass.setColorAttachmentCount(1); - subpass.setPColorAttachments(&references[index++]); - } - - if (depth_format != vk::Format::eUndefined) { - references[index] = vk::AttachmentReference{index, vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments[index] = - { - {}, - depth_format, - static_cast(multisamples), - load_op, - vk::AttachmentStoreOp::eStore, - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - vk::ImageLayout::eDepthStencilAttachmentOptimal, - vk::ImageLayout::eDepthStencilAttachmentOptimal - }; - - subpass.setPDepthStencilAttachment(&references[index++]); - } - - std::array subpasses = { subpass }; - vk::RenderPassCreateInfo renderpass_info({}, attachments, subpasses); - - auto renderpass = g_vk_instace->GetDevice().createRenderPassUnique(renderpass_info); - vk::RenderPass handle = renderpass.get(); - - renderpass_cache.emplace(key, std::move(renderpass)); - return handle; -} - -Pipeline::Pipeline() { Clear(); } - -void Pipeline::Clear() -{ - m_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - m_ci.pNext = nullptr; - m_ci.flags = 0; - m_ci.pSetLayouts = nullptr; - m_ci.setLayoutCount = 0; - m_ci.pPushConstantRanges = nullptr; - m_ci.pushConstantRangeCount = 0; -} - -void Pipeline::Build() { - VkPipelineLayout layout; - VkResult res = vkCreatePipelineLayout(device, &m_ci, nullptr, &layout); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout() failed: "); - return VK_NULL_HANDLE; - } - - Clear(); - return layout; -} - -void Pipeline::AddDescriptorSet(VkDescriptorSetLayout layout) -{ - pxAssert(m_ci.setLayoutCount < MAX_SETS); - - m_sets[m_ci.setLayoutCount] = layout; - - m_ci.setLayoutCount++; - m_ci.pSetLayouts = m_sets.data(); -} - -void Pipeline::AddPushConstants(VkShaderStageFlags stages, u32 offset, u32 size) -{ - pxAssert(m_ci.pushConstantRangeCount < MAX_PUSH_CONSTANTS); - - VkPushConstantRange& r = m_push_constants[m_ci.pushConstantRangeCount]; - r.stageFlags = stages; - r.offset = offset; - r.size = size; - - m_ci.pushConstantRangeCount++; - m_ci.pPushConstantRanges = m_push_constants.data(); -} - -GraphicsPipelineBuilder::GraphicsPipelineBuilder() { Clear(); } - -void GraphicsPipelineBuilder::Clear() -{ - m_ci = {}; - m_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - - m_shader_stages = {}; - - m_vertex_input_state = {}; - m_vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - m_ci.pVertexInputState = &m_vertex_input_state; - m_vertex_attributes = {}; - m_vertex_buffers = {}; - - m_input_assembly = {}; - m_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - - m_rasterization_state = {}; - m_rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - m_rasterization_state.lineWidth = 1.0f; - m_depth_state = {}; - m_depth_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - m_blend_state = {}; - m_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - m_blend_attachments = {}; - - m_viewport_state = {}; - m_viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - m_viewport = {}; - m_scissor = {}; - - m_dynamic_state = {}; - m_dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - m_dynamic_state_values = {}; - - m_multisample_state = {}; - m_multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - - m_provoking_vertex = {}; - m_provoking_vertex.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT; - - // set defaults - SetNoCullRasterizationState(); - SetNoDepthTestState(); - SetNoBlendingState(); - SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); - - // have to be specified even if dynamic - SetViewport(0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f); - SetScissorRect(0, 0, 1, 1); - SetMultisamples(VK_SAMPLE_COUNT_1_BIT); -} - -VkPipeline GraphicsPipelineBuilder::Create(VkDevice device, VkPipelineCache pipeline_cache, bool clear /* = true */) -{ - VkPipeline pipeline; - VkResult res = vkCreateGraphicsPipelines(device, pipeline_cache, 1, &m_ci, nullptr, &pipeline); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateGraphicsPipelines() failed: "); - return VK_NULL_HANDLE; - } - - if (clear) - Clear(); - - return pipeline; -} - -void GraphicsPipelineBuilder::SetShaderStage( - VkShaderStageFlagBits stage, VkShaderModule module, const char* entry_point) -{ - pxAssert(m_ci.stageCount < MAX_SHADER_STAGES); - - u32 index = 0; - for (; index < m_ci.stageCount; index++) - { - if (m_shader_stages[index].stage == stage) - break; - } - if (index == m_ci.stageCount) - { - m_ci.stageCount++; - m_ci.pStages = m_shader_stages.data(); - } - - VkPipelineShaderStageCreateInfo& s = m_shader_stages[index]; - s.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - s.stage = stage; - s.module = module; - s.pName = entry_point; -} - -void GraphicsPipelineBuilder::AddVertexBuffer( - u32 binding, u32 stride, VkVertexInputRate input_rate /*= VK_VERTEX_INPUT_RATE_VERTEX*/) -{ - pxAssert(m_vertex_input_state.vertexAttributeDescriptionCount < MAX_VERTEX_BUFFERS); - - VkVertexInputBindingDescription& b = m_vertex_buffers[m_vertex_input_state.vertexBindingDescriptionCount]; - b.binding = binding; - b.stride = stride; - b.inputRate = input_rate; - - m_vertex_input_state.vertexBindingDescriptionCount++; - m_vertex_input_state.pVertexBindingDescriptions = m_vertex_buffers.data(); - m_ci.pVertexInputState = &m_vertex_input_state; -} - -void GraphicsPipelineBuilder::AddVertexAttribute(u32 location, u32 binding, VkFormat format, u32 offset) -{ - pxAssert(m_vertex_input_state.vertexAttributeDescriptionCount < MAX_VERTEX_BUFFERS); - - VkVertexInputAttributeDescription& a = - m_vertex_attributes[m_vertex_input_state.vertexAttributeDescriptionCount]; - a.location = location; - a.binding = binding; - a.format = format; - a.offset = offset; - - m_vertex_input_state.vertexAttributeDescriptionCount++; - m_vertex_input_state.pVertexAttributeDescriptions = m_vertex_attributes.data(); - m_ci.pVertexInputState = &m_vertex_input_state; -} - -void GraphicsPipelineBuilder::SetPrimitiveTopology( - VkPrimitiveTopology topology, bool enable_primitive_restart /*= false*/) -{ - m_input_assembly.topology = topology; - m_input_assembly.primitiveRestartEnable = enable_primitive_restart; - - m_ci.pInputAssemblyState = &m_input_assembly; -} - -void GraphicsPipelineBuilder::SetRasterizationState( - VkPolygonMode polygon_mode, VkCullModeFlags cull_mode, VkFrontFace front_face) -{ - m_rasterization_state.polygonMode = polygon_mode; - m_rasterization_state.cullMode = cull_mode; - m_rasterization_state.frontFace = front_face; - - m_ci.pRasterizationState = &m_rasterization_state; -} - -void GraphicsPipelineBuilder::SetLineWidth(float width) { m_rasterization_state.lineWidth = width; } - -void GraphicsPipelineBuilder::SetMultisamples(u32 multisamples, bool per_sample_shading) -{ - m_multisample_state.rasterizationSamples = static_cast(multisamples); - m_multisample_state.sampleShadingEnable = per_sample_shading; - m_multisample_state.minSampleShading = (multisamples > 1) ? 1.0f : 0.0f; -} - -void GraphicsPipelineBuilder::SetNoCullRasterizationState() -{ - SetRasterizationState(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); -} - -void GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, VkCompareOp compare_op) -{ - m_depth_state.depthTestEnable = depth_test; - m_depth_state.depthWriteEnable = depth_write; - m_depth_state.depthCompareOp = compare_op; - - m_ci.pDepthStencilState = &m_depth_state; -} - -void GraphicsPipelineBuilder::SetStencilState( - bool stencil_test, const VkStencilOpState& front, const VkStencilOpState& back) -{ - m_depth_state.stencilTestEnable = stencil_test; - m_depth_state.front = front; - m_depth_state.back = back; -} - -void GraphicsPipelineBuilder::SetNoStencilState() -{ - m_depth_state.stencilTestEnable = VK_FALSE; - m_depth_state.front = {}; - m_depth_state.back = {}; -} - -void GraphicsPipelineBuilder::SetNoDepthTestState() { SetDepthState(false, false, VK_COMPARE_OP_ALWAYS); } - -void GraphicsPipelineBuilder::SetBlendConstants(float r, float g, float b, float a) -{ - m_blend_state.blendConstants[0] = r; - m_blend_state.blendConstants[1] = g; - m_blend_state.blendConstants[2] = b; - m_blend_state.blendConstants[3] = a; - m_ci.pColorBlendState = &m_blend_state; -} - -void GraphicsPipelineBuilder::AddBlendAttachment(bool blend_enable, VkBlendFactor src_factor, - VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor, - VkBlendOp alpha_op, - VkColorComponentFlags - write_mask /* = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT */) -{ - pxAssert(m_blend_state.attachmentCount < MAX_ATTACHMENTS); - - VkPipelineColorBlendAttachmentState& bs = m_blend_attachments[m_blend_state.attachmentCount]; - bs.blendEnable = blend_enable; - bs.srcColorBlendFactor = src_factor; - bs.dstColorBlendFactor = dst_factor; - bs.colorBlendOp = op; - bs.srcAlphaBlendFactor = alpha_src_factor; - bs.dstAlphaBlendFactor = alpha_dst_factor; - bs.alphaBlendOp = alpha_op; - bs.colorWriteMask = write_mask; - - m_blend_state.attachmentCount++; - m_blend_state.pAttachments = m_blend_attachments.data(); - m_ci.pColorBlendState = &m_blend_state; -} - -void GraphicsPipelineBuilder::SetBlendAttachment(u32 attachment, bool blend_enable, VkBlendFactor src_factor, - VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor, - VkBlendOp alpha_op, - VkColorComponentFlags - write_mask /*= VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT*/) -{ - pxAssert(attachment < MAX_ATTACHMENTS); - - VkPipelineColorBlendAttachmentState& bs = m_blend_attachments[attachment]; - bs.blendEnable = blend_enable; - bs.srcColorBlendFactor = src_factor; - bs.dstColorBlendFactor = dst_factor; - bs.colorBlendOp = op; - bs.srcAlphaBlendFactor = alpha_src_factor; - bs.dstAlphaBlendFactor = alpha_dst_factor; - bs.alphaBlendOp = alpha_op; - bs.colorWriteMask = write_mask; - - if (attachment >= m_blend_state.attachmentCount) - { - m_blend_state.attachmentCount = attachment + 1u; - m_blend_state.pAttachments = m_blend_attachments.data(); - m_ci.pColorBlendState = &m_blend_state; - } -} - -void GraphicsPipelineBuilder::AddBlendFlags(u32 flags) -{ - m_blend_state.flags |= flags; -} - -void GraphicsPipelineBuilder::ClearBlendAttachments() -{ - m_blend_attachments = {}; - m_blend_state.attachmentCount = 0; -} - -void GraphicsPipelineBuilder::SetNoBlendingState() -{ - ClearBlendAttachments(); - SetBlendAttachment(0, false, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, - VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, - VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); -} - -void GraphicsPipelineBuilder::AddDynamicState(VkDynamicState state) -{ - pxAssert(m_dynamic_state.dynamicStateCount < MAX_DYNAMIC_STATE); - - m_dynamic_state_values[m_dynamic_state.dynamicStateCount] = state; - m_dynamic_state.dynamicStateCount++; - m_dynamic_state.pDynamicStates = m_dynamic_state_values.data(); - m_ci.pDynamicState = &m_dynamic_state; -} - -void GraphicsPipelineBuilder::SetDynamicViewportAndScissorState() -{ - AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT); - AddDynamicState(VK_DYNAMIC_STATE_SCISSOR); -} - -void GraphicsPipelineBuilder::SetViewport( - float x, float y, float width, float height, float min_depth, float max_depth) -{ - m_viewport.x = x; - m_viewport.y = y; - m_viewport.width = width; - m_viewport.height = height; - m_viewport.minDepth = min_depth; - m_viewport.maxDepth = max_depth; - - m_viewport_state.pViewports = &m_viewport; - m_viewport_state.viewportCount = 1u; - m_ci.pViewportState = &m_viewport_state; -} - -void GraphicsPipelineBuilder::SetScissorRect(s32 x, s32 y, u32 width, u32 height) -{ - m_scissor.offset.x = x; - m_scissor.offset.y = y; - m_scissor.extent.width = width; - m_scissor.extent.height = height; - - m_viewport_state.pScissors = &m_scissor; - m_viewport_state.scissorCount = 1u; - m_ci.pViewportState = &m_viewport_state; -} - -void GraphicsPipelineBuilder::SetMultisamples(VkSampleCountFlagBits samples) -{ - m_multisample_state.rasterizationSamples = samples; - m_ci.pMultisampleState = &m_multisample_state; -} - -void GraphicsPipelineBuilder::SetPipelineLayout(VkPipelineLayout layout) { m_ci.layout = layout; } - -void GraphicsPipelineBuilder::SetRenderPass(VkRenderPass render_pass, u32 subpass) -{ - m_ci.renderPass = render_pass; - m_ci.subpass = subpass; -} - -void GraphicsPipelineBuilder::SetProvokingVertex(VkProvokingVertexModeEXT mode) -{ - Util::AddPointerToChain(&m_rasterization_state, &m_provoking_vertex); - - m_provoking_vertex.provokingVertexMode = mode; -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.h b/src/video_core/renderer_vulkan/vk_resource_cache.h deleted file mode 100644 index 09d52bdce..000000000 --- a/src/video_core/renderer_vulkan/vk_resource_cache.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2022 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "video_core/renderer_vulkan/vk_texture.h" - -namespace Vulkan { - -struct RenderPassCacheKey { - vk::Format color, depth; - vk::SampleCountFlagBits samples; -}; - -constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3; - -/// Wrapper class that manages resource caching and storage. -/// It stores pipelines and renderpasses -class VKResourceCache { -public: - VKResourceCache() = default; - ~VKResourceCache(); - - // Perform at startup, create descriptor layouts, compiles all static shaders. - bool Initialize(); - void Shutdown(); - - // Public interface. - vk::PipelineCache GetPipelineCache() const { return pipeline_cache.get(); } - vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format, - vk::SampleCountFlagBits multisamples, - vk::AttachmentLoadOp load_op); - - auto& GetDescriptorLayouts() const { return descriptor_layouts; } - -private: - // Descriptor sets - std::array descriptor_layouts; - vk::UniquePipelineLayout pipeline_layout; - - // Render pass cache - std::unordered_map renderpass_cache; - - vk::UniquePipelineCache pipeline_cache; - std::string pipeline_cache_filename; -}; - -constexpr u32 MAX_DYNAMIC_STATES = 8; -constexpr u32 MAX_ATTACHMENTS = 2; -constexpr u32 MAX_VERTEX_BUFFERS = 3; - -class Pipeline { -public: - Pipeline(); - ~Pipeline() = default; - - void Build(); - - void SetShaderStage(vk::ShaderStageFlagBits stage, vk::ShaderModule module); - - void AddVertexBuffer(u32 binding, u32 stride, vk::VertexInputRate input_rate); - void AddVertexAttribute(u32 location, u32 binding, VkFormat format, u32 offset); - - void SetPrimitiveTopology(vk::PrimitiveTopology topology, bool enable_primitive_restart = false); - void SetRasterizationState(vk::PolygonMode polygon_mode, vk::CullModeFlags cull_mode, - vk::FrontFace front_face); - - void SetDepthState(bool depth_test, bool depth_write, vk::CompareOp compare_op); - void SetStencilState(bool stencil_test, vk::StencilOpState front, vk::StencilOpState back); - void SetNoDepthTestState(); - void SetNoStencilState(); - - void AddDynamicState(vk::DynamicState state); - void SetMultisamples(VkSampleCountFlagBits samples); - -private: - vk::GraphicsPipelineCreateInfo pipeline_info; - std::array shader_stages; - - vk::PipelineVertexInputStateCreateInfo vertex_input_state; - vk::PipelineInputAssemblyStateCreateInfo input_assembly; - vk::PipelineRasterizationStateCreateInfo rasterization_state; - vk::PipelineDepthStencilStateCreateInfo depth_state; - - // Blending - vk::PipelineColorBlendStateCreateInfo blend_state; - std::array blend_attachments; - std::array dynamic_state_values; - - VkPipelineViewportStateCreateInfo m_viewport_state; - VkViewport m_viewport; - VkRect2D m_scissor; - - VkPipelineDynamicStateCreateInfo m_dynamic_state; - vk::PipelineMultisampleStateCreateInfo multisample_info; -}; - -extern std::unique_ptr g_vk_res_cache; - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_state.h b/src/video_core/renderer_vulkan/vk_shader_state.h index 62572b1aa..1c8da93e4 100644 --- a/src/video_core/renderer_vulkan/vk_shader_state.h +++ b/src/video_core/renderer_vulkan/vk_shader_state.h @@ -11,12 +11,73 @@ #include #include #include +#include #include "common/hash.h" #include "video_core/regs.h" #include "video_core/shader/shader.h" namespace Vulkan { +/* Vertex attributes */ +struct VertexBase { + VertexBase() = default; + VertexBase(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { + position[0] = v.pos.x.ToFloat32(); + position[1] = v.pos.y.ToFloat32(); + position[2] = v.pos.z.ToFloat32(); + position[3] = v.pos.w.ToFloat32(); + color[0] = v.color.x.ToFloat32(); + color[1] = v.color.y.ToFloat32(); + color[2] = v.color.z.ToFloat32(); + color[3] = v.color.w.ToFloat32(); + tex_coord0[0] = v.tc0.x.ToFloat32(); + tex_coord0[1] = v.tc0.y.ToFloat32(); + tex_coord1[0] = v.tc1.x.ToFloat32(); + tex_coord1[1] = v.tc1.y.ToFloat32(); + tex_coord2[0] = v.tc2.x.ToFloat32(); + tex_coord2[1] = v.tc2.y.ToFloat32(); + tex_coord0_w = v.tc0_w.ToFloat32(); + normquat[0] = v.quat.x.ToFloat32(); + normquat[1] = v.quat.y.ToFloat32(); + normquat[2] = v.quat.z.ToFloat32(); + normquat[3] = v.quat.w.ToFloat32(); + view[0] = v.view.x.ToFloat32(); + view[1] = v.view.y.ToFloat32(); + view[2] = v.view.z.ToFloat32(); + + if (flip_quaternion) { + normquat = -normquat; + } + } + + glm::vec4 position; + glm::vec4 color; + glm::vec2 tex_coord0; + glm::vec2 tex_coord1; + glm::vec2 tex_coord2; + float tex_coord0_w; + glm::vec4 normquat; + glm::vec3 view; +}; + +/// Structure that the hardware rendered vertices are composed of +struct HardwareVertex : public VertexBase { + HardwareVertex() = default; + HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) : VertexBase(v, flip_quaternion) {}; + static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(VertexBase)); + static constexpr std::array attribute_desc = + { + vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, position)), + vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, color)), + vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord0)), + vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord1)), + vk::VertexInputAttributeDescription(4, 0, vk::Format::eR32G32Sfloat, offsetof(VertexBase, tex_coord2)), + vk::VertexInputAttributeDescription(5, 0, vk::Format::eR32Sfloat, offsetof(VertexBase, tex_coord0_w)), + vk::VertexInputAttributeDescription(6, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(VertexBase, normquat)), + vk::VertexInputAttributeDescription(7, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexBase, view)), + }; +}; + enum class ProgramType : u32 { VS, GS, FS }; enum Attributes { diff --git a/src/video_core/renderer_vulkan/vk_state.cpp b/src/video_core/renderer_vulkan/vk_state.cpp index 39b696bb2..084624940 100644 --- a/src/video_core/renderer_vulkan/vk_state.cpp +++ b/src/video_core/renderer_vulkan/vk_state.cpp @@ -6,7 +6,6 @@ #include #include "video_core/renderer_vulkan/vk_state.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" -#include "video_core/renderer_vulkan/vk_resource_cache.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_shader_gen.h" @@ -60,11 +59,6 @@ void VulkanState::Create() { vk::DescriptorPoolCreateInfo pool_create_info({}, 1024, pool_sizes); desc_pool = device.createDescriptorPoolUnique(pool_create_info); - // Create descriptor sets - auto& layouts = g_vk_res_cache->GetDescriptorLayouts(); - vk::DescriptorSetAllocateInfo alloc_info(desc_pool.get(), layouts); - descriptor_sets = device.allocateDescriptorSetsUnique(alloc_info); - // Create texture sampler auto props = g_vk_instace->GetPhysicalDevice().getProperties(); vk::SamplerCreateInfo sampler_info{ @@ -75,47 +69,15 @@ void VulkanState::Create() { false, vk::CompareOp::eAlways, {}, {}, vk::BorderColor::eIntOpaqueBlack, false }; - sampler = g_vk_instace->GetDevice().createSamplerUnique(sampler_info); - - - // Define the descriptor sets we will be using - std::array ubo_set = {{ - { 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eFragment }, // shader_data - { 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex } // pica_uniforms - }}; - - std::array texture_set = {{ - { 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex0 - { 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex1 - { 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex2 - { 3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex_cube - }}; - - std::array lut_set = {{ - { 0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_lf - { 1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_rg - { 2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment } // texture_buffer_lut_rgba - }}; - - // Create and store descriptor set layouts - std::array create_infos = {{ - { vk::DescriptorSetLayoutCreateFlags(), ubo_set }, - { vk::DescriptorSetLayoutCreateFlags(), texture_set }, - { vk::DescriptorSetLayoutCreateFlags(), lut_set } - }}; - - for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) { - descriptor_layouts[i] = g_vk_instace->GetDevice().createDescriptorSetLayout(create_infos[i]); - } - - // Create the standard descriptor set layout - vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts); - pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info); + sampler = device.createSamplerUnique(sampler_info); // Compile trivial vertex shader auto source = GenerateTrivialVertexShader(true); - MakeShader(source.code, vk::ShaderStageFlagBits::eVertex); + trivial_vertex_shader = vk::UniqueShaderModule{CompileShader(source.code, vk::ShaderStageFlagBits::eVertex)}; + + // Configure descriptor sets and pipeline builder + ConfigureDescriptorSets(); + ConfigurePipeline(); dirty_flags |= DirtyFlags::All; } @@ -186,13 +148,14 @@ void VulkanState::UnbindTexture(u32 index) { dirty_flags |= DirtyFlags::Texture; } -void VulkanState::PushRenderTargets(VKTexture* color, VKTexture* depth_stencil) { - color_attachment = color; - depth_attachment = depth_stencil; +void VulkanState::PushAttachment(Attachment attachment) { + targets.push_back(attachment); } -void VulkanState::SetRenderArea(vk::Rect2D new_render_area) { - render_area = new_render_area; +void VulkanState::PopAttachment() { + if (!targets.empty()) { + targets.pop_back(); + } } void VulkanState::BeginRendering() { @@ -201,21 +164,27 @@ void VulkanState::BeginRendering() { } // Make sure attachments are in optimal layout - color_attachment->Transition(vk::ImageLayout::eColorAttachmentOptimal); - depth_attachment->Transition(vk::ImageLayout::eDepthStencilAttachmentOptimal); + auto& attachment = targets.back(); + vk::RenderingInfo render_info{{}, attachment.render_area, 1, {}}; + std::array infos{}; + + if (attachment.color) { + attachment.color->Transition(vk::ImageLayout::eColorAttachmentOptimal); + + infos[0] = {attachment.color->GetView(), attachment.color->GetLayout()}; + render_info.colorAttachmentCount = 1; + render_info.pColorAttachments = &infos[0]; + } + + if (attachment.depth_stencil) { + attachment.depth_stencil->Transition(vk::ImageLayout::eDepthStencilAttachmentOptimal); + + infos[1] = {attachment.depth_stencil->GetView(), attachment.depth_stencil->GetLayout()}; + render_info.pDepthAttachment = &infos[1]; + render_info.pStencilAttachment = &infos[1]; + } // Begin rendering - vk::RenderingAttachmentInfoKHR color_info(color_attachment->GetView(), color_attachment->GetLayout()); - vk::RenderingAttachmentInfoKHR depth_stencil_info(depth_attachment->GetView(), depth_attachment->GetLayout()); - - vk::RenderingInfo render_info - ( - {}, render_area, 1, {}, - color_info, - &depth_stencil_info, - &depth_stencil_info - ); - auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); command_buffer.beginRendering(render_info); rendering = true; @@ -304,7 +273,7 @@ void VulkanState::SetStencilTest(bool enable, vk::StencilOp fail, vk::StencilOp fail_op = fail; pass_op = pass; depth_fail_op = depth_fail; - compare_op = compare; + stencil_op = compare; dirty_flags |= DirtyFlags::Stencil; } @@ -317,7 +286,7 @@ void VulkanState::SetDepthWrite(bool enable) { void VulkanState::SetDepthTest(bool enable, vk::CompareOp compare) { depth_enabled = enable; - test_func = compare; + depth_op = compare; dirty_flags |= DirtyFlags::DepthTest; } @@ -333,36 +302,41 @@ void VulkanState::SetBlendOp(vk::BlendOp rgb_op, vk::BlendOp alpha_op, vk::Blend } void VulkanState::SetFragmentShader(const Pica::Regs& regs) { - vk::Pipeline pipeline; pipeline_key.fragment_config = PicaFSConfig::BuildFromRegs(regs); auto it1 = pipelines.find(pipeline_key); - do { - // Try to use an already complete pipeline - if (it1 != pipelines.end()) { - pipeline = it1->second.get(); - break; - } - + // Try to use an already complete pipeline + vk::Pipeline pipeline; + if (it1 != pipelines.end()) { + pipeline = it1->second.get(); + } + else { // Maybe the shader has been compiled but the pipeline state changed? auto shader = fragment_shaders.find(pipeline_key.fragment_config); if (shader != fragment_shaders.end()) { - pipeline = MakePipeline(shader->second.get()); - break; + builder.SetShaderStage(vk::ShaderStageFlagBits::eFragment, shader->second.get()); + pipeline = builder.Build(); + } + else { + // Re-compile shader module and create new pipeline + auto result = GenerateFragmentShader(pipeline_key.fragment_config); + auto module = CompileShader(result.code, vk::ShaderStageFlagBits::eFragment); + fragment_shaders.emplace(pipeline_key.fragment_config, vk::UniqueShaderModule{module}); + + builder.SetShaderStage(vk::ShaderStageFlagBits::eFragment, shader->second.get()); + pipeline = builder.Build(); } - // Re-compile shader module and create new pipeline - auto result = GenerateFragmentShader(pipeline_key.fragment_config); - auto module = MakeShader(result.code, vk::ShaderStageFlagBits::eFragment); - pipeline = MakePipeline(module); - } while (false); + // Cache the resulted pipeline + pipelines.emplace(pipeline_key, vk::UniquePipeline{pipeline}); + } // Bind the pipeline auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); } -vk::ShaderModule VulkanState::MakeShader(const std::string& source, vk::ShaderStageFlagBits stage) { +vk::ShaderModule VulkanState::CompileShader(const std::string& source, vk::ShaderStageFlagBits stage) { shaderc::Compiler compiler; shaderc::CompileOptions options; options.SetOptimizationLevel(shaderc_optimization_level_performance); @@ -393,73 +367,8 @@ vk::ShaderModule VulkanState::MakeShader(const std::string& source, vk::ShaderSt vk::ShaderModuleCreateInfo shader_info{{}, shader_code}; auto& device = g_vk_instace->GetDevice(); - auto shader = device.createShaderModuleUnique(shader_info); - - if (stage == vk::ShaderStageFlagBits::eFragment) { - auto handle = shader.get(); - fragment_shaders[pipeline_key.fragment_config] = std::move(shader); - return handle; - } - else if (stage == vk::ShaderStageFlagBits::eVertex) { - trivial_vertex_shader = std::move(shader); - return trivial_vertex_shader.get(); - } - - UNREACHABLE(); -} - -vk::Pipeline VulkanState::MakePipeline(vk::ShaderModule fragment) { - std::array shader_stages {{ - { {}, vk::ShaderStageFlagBits::eVertex, trivial_vertex_shader.get(), "main" }, - { {}, vk::ShaderStageFlagBits::eFragment, fragment, "main" } - }}; - - vk::PipelineVertexInputStateCreateInfo vertex_input_info{ - {}, HardwareVertex::binding_desc, HardwareVertex::attribute_desc - }; - - vk::PipelineInputAssemblyStateCreateInfo input_assembly{{}, vk::PrimitiveTopology::eTriangleList, false}; - vk::PipelineRasterizationStateCreateInfo rasterizer{ - {}, false, false, vk::PolygonMode::eFill, vk::CullModeFlagBits::eNone, - vk::FrontFace::eClockwise, false - }; - - vk::PipelineMultisampleStateCreateInfo multisampling{{}, vk::SampleCountFlagBits::e1}; - vk::PipelineColorBlendStateCreateInfo color_blending{{}, false, vk::LogicOp::eCopy, pipeline_key.blend_config}; - - // Enable every required dynamic state - std::array dynamic_states{ - vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth, - vk::DynamicState::eDepthTestEnable, vk::DynamicState::eColorWriteEnableEXT, - vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilOp, - vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, - vk::DynamicState::eCullMode, vk::DynamicState::eBlendConstants, - vk::DynamicState::eViewport, vk::DynamicState::eScissor, - vk::DynamicState::eLogicOpEXT, vk::DynamicState::eFrontFace - }; - - vk::PipelineDynamicStateCreateInfo dynamic_info{{}, dynamic_states}; - - vk::PipelineDepthStencilStateCreateInfo depth_info{ - {}, true, true, vk::CompareOp::eGreaterOrEqual, false, true - }; - - vk::GraphicsPipelineCreateInfo pipeline_info{ - {}, shader_stages, &vertex_input_info, &input_assembly, nullptr, nullptr, - &rasterizer, &multisampling, &depth_info, &color_blending, &dynamic_info, - pipeline_layout.get(), nullptr - }; - - auto& device = g_vk_instace->GetDevice(); - auto result = device.createGraphicsPipelineUnique(nullptr, pipeline_info); - - if (result.result == vk::Result::eSuccess) { - auto handle = result.value.get(); - pipelines[pipeline_key] = std::move(result.value); - return handle; - } - - return VK_NULL_HANDLE; + auto shader = device.createShaderModule(shader_info); + return shader; } void VulkanState::Apply() { @@ -487,9 +396,126 @@ void VulkanState::Apply() { command_buffer.setScissor(0, scissor); } + if (dirty_flags & DirtyFlags::DepthTest) { + command_buffer.setDepthTestEnable(depth_enabled); + command_buffer.setDepthCompareOp(depth_op); + } + + if (dirty_flags & DirtyFlags::Stencil) { + command_buffer.setStencilTestEnable(stencil_enabled); + command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_ref); + command_buffer.setStencilOp(vk::StencilFaceFlagBits::eFrontAndBack, fail_op, pass_op, + depth_fail_op, stencil_op); + } + + if (dirty_flags & DirtyFlags::LogicOp) { + command_buffer.setLogicOpEXT(logic_op); + } + + if (dirty_flags & DirtyFlags::CullMode) { + command_buffer.setCullMode(cull_mode); + } + + if (dirty_flags & DirtyFlags::FrontFace) { + command_buffer.setFrontFace(front_face); + } + + if (dirty_flags & DirtyFlags::BlendConsts) { + command_buffer.setBlendConstants(blend_constants.data()); + } + + if (dirty_flags & DirtyFlags::StencilMask) { + command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, stencil_write_mask); + command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, stencil_input_mask); + } + + if (dirty_flags & DirtyFlags::DepthWrite) { + command_buffer.setDepthWriteEnable(depth_writes); + } + dirty_flags = DirtyFlags::None; } +void VulkanState::ConfigureDescriptorSets() { + // Define the descriptor sets we will be using + std::array ubo_set = {{ + { 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | + vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eFragment }, // shader_data + { 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex } // pica_uniforms + }}; + + std::array texture_set = {{ + { 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex0 + { 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex1 + { 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex2 + { 3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex_cube + }}; + + std::array lut_set = {{ + { 0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_lf + { 1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_rg + { 2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment } // texture_buffer_lut_rgba + }}; + + // Create and store descriptor set layouts + std::array create_infos{{ + { vk::DescriptorSetLayoutCreateFlags(), ubo_set }, + { vk::DescriptorSetLayoutCreateFlags(), texture_set }, + { vk::DescriptorSetLayoutCreateFlags(), lut_set } + }}; + + auto& device = g_vk_instace->GetDevice(); + for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) { + descriptor_layouts[i] = device.createDescriptorSetLayout(create_infos[i]); + } + + vk::DescriptorSetAllocateInfo alloc_info(desc_pool.get(), descriptor_layouts); + descriptor_sets = device.allocateDescriptorSetsUnique(alloc_info); + + // Create the standard descriptor set layout + vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts); + pipeline_layout = device.createPipelineLayoutUnique(layout_info); + +} + +void VulkanState::ConfigurePipeline() { + builder.SetPipelineLayout(pipeline_layout.get()); + + // Set rasterization state + builder.SetPrimitiveTopology(vk::PrimitiveTopology::eTriangleList); + builder.SetLineWidth(1.0f); + builder.SetRasterizationState(vk::PolygonMode::eFill, vk::CullModeFlagBits::eNone, + vk::FrontFace::eClockwise); + + // Set depth, stencil tests and blending + builder.SetNoDepthTestState(); + builder.SetNoStencilState(); + builder.SetBlendConstants(1.f, 1.f, 1.f, 1.f); + builder.SetBlendAttachment(true, vk::BlendFactor::eOne, vk::BlendFactor::eZero, vk::BlendOp::eAdd, + vk::BlendFactor::eOne, vk::BlendFactor::eZero, vk::BlendOp::eAdd, + vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); + + + // Enable every required dynamic state + std::array dynamic_states{ + vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth, + vk::DynamicState::eDepthTestEnable, vk::DynamicState::eColorWriteEnableEXT, + vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilOp, + vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eCullMode, vk::DynamicState::eBlendConstants, + vk::DynamicState::eViewport, vk::DynamicState::eScissor, + vk::DynamicState::eLogicOpEXT, vk::DynamicState::eFrontFace + }; + + for (auto& state : dynamic_states) { + builder.AddDynamicState(state); + } + + // Add trivial vertex shader + builder.SetShaderStage(vk::ShaderStageFlagBits::eVertex, trivial_vertex_shader.get()); +} + void VulkanState::UpdateDescriptorSet() { std::vector writes; std::vector buffer_infos; diff --git a/src/video_core/renderer_vulkan/vk_state.h b/src/video_core/renderer_vulkan/vk_state.h index 9a46f35ee..1fb0d9d86 100644 --- a/src/video_core/renderer_vulkan/vk_state.h +++ b/src/video_core/renderer_vulkan/vk_state.h @@ -9,6 +9,7 @@ #include #include "video_core/regs.h" #include "video_core/renderer_vulkan/vk_shader_state.h" +#include "video_core/renderer_vulkan/vk_pipeline_builder.h" #include "video_core/renderer_vulkan/vk_texture.h" namespace Vulkan { @@ -54,6 +55,15 @@ BindingID operator + (BindingID lhs, u32 rhs) { return static_cast(static_cast(lhs) + rhs); } +struct Attachment { + VKTexture* color{}, *depth_stencil{}; + vk::ClearColorValue clear_color; + vk::ClearDepthStencilValue depth_color; + vk::Rect2D render_area{-1}; +}; + +constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3; + /// Tracks global Vulkan state class VulkanState { public: @@ -87,9 +97,8 @@ public: vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha); /// Rendering - void PushRenderTargets(VKTexture* color, VKTexture* depth_stencil); - void PopRenderTargets(); - void SetRenderArea(vk::Rect2D render_area); + void PushAttachment(Attachment attachment); + void PopAttachment(); void SetFragmentShader(const Pica::Regs& config); void BeginRendering(); void EndRendering(); @@ -105,9 +114,10 @@ public: void Apply(); private: + void ConfigureDescriptorSets(); + void ConfigurePipeline(); void UpdateDescriptorSet(); - vk::Pipeline MakePipeline(vk::ShaderModule fragment); - vk::ShaderModule MakeShader(const std::string& source, vk::ShaderStageFlagBits stage); + vk::ShaderModule CompileShader(const std::string& source, vk::ShaderStageFlagBits stage); private: struct Binding { @@ -116,40 +126,32 @@ private: vk::UniqueBufferView buffer_view{}; }; - struct Attachment { - VKTexture* color{}; - VKTexture* depth_stencil{}; - }; - DirtyFlags dirty_flags; bool rendering = false; VKTexture dummy_texture; vk::UniqueSampler sampler; + std::vector targets; VKBuffer* vertex_buffer{}, * index_buffer{}; - vk::DeviceSize vertex_offset{}, index_offset{}; - std::array bindings{}; - std::vector descriptor_sets{}; + vk::DeviceSize vertex_offset, index_offset; + std::array bindings; + std::vector descriptor_sets; vk::UniqueDescriptorPool desc_pool; - vk::Viewport viewport{ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + vk::Viewport viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; vk::CullModeFlags cull_mode{}; vk::FrontFace front_face{}; vk::Rect2D scissor{}; vk::LogicOp logic_op{}; std::array blend_constants{}; - VKTexture* color_attachment{}, * depth_attachment{}; - vk::Rect2D render_area{}; - bool depth_enabled, depth_writes; - vk::CompareOp test_func; - u32 stencil_write_mask{}, stencil_input_mask{}, stencil_ref{}; - bool stencil_enabled{}, stencil_writes{}; + bool depth_enabled{}, depth_writes{}, stencil_enabled{}, stencil_writes{}; vk::StencilOp fail_op, pass_op, depth_fail_op; - vk::CompareOp compare_op; + vk::CompareOp depth_op, stencil_op; // Pipeline cache + PipelineBuilder builder; vk::UniqueShaderModule trivial_vertex_shader; vk::UniquePipelineLayout pipeline_layout; std::vector descriptor_layouts; diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 69f5d7baf..64ceabf0d 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -164,18 +164,18 @@ void VKTexture::Transition(vk::ImageLayout new_layout) { } void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span pixels) { - u8* staging = g_vk_task_scheduler->RequestStaging(pixels.size()); - if (!staging) { + auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(pixels.size()); + if (!buffer) { LOG_ERROR(Render_Vulkan, "Cannot copy pixels without staging buffer!"); } auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); // Copy pixels to staging buffer - std::memcpy(staging, pixels.data(), pixels.size()); + std::memcpy(buffer, pixels.data(), pixels.size()); - vk::BufferImageCopy copy_region { - 0, row_length, region.extent.height, + vk::BufferImageCopy copy_region{ + offset, row_length, region.extent.height, {info.aspect, level, layer, 1}, { region.offset.x, region.offset.y, 0 }, { region.extent.width, region.extent.height, 1 } diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index 72b4db670..ea5e9c768 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -49,6 +49,8 @@ public: vk::Format GetFormat() const { return info.format; } vk::ImageLayout GetLayout() const { return layout; } u32 GetSamples() const { return info.multisamples; } + u32 GetSize() const { return image_size; } + vk::Extent2D GetExtent() const { return {info.width, info.height}; } /// Copies CPU side pixel data to the GPU texture buffer void Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span pixels);